mybatis多個參數(不使用@param註解狀況下),sql參數佔位符正確寫法

useActualParamName配置

useActualParamName 容許使用方法簽名中的名稱做爲語句參數名稱。 爲了使用該特性,你的工程必須採用Java 8編譯,而且加上-parameters選項。(從3.4.1開始) true | false true

mybatis的全局配置useActualParamName決定了mapper中參數的寫法,默認爲truesql

代碼展現:

@Test
public void findUserById() {
    SqlSession sqlSession = getSessionFactory().openSession();
    UserDao userMapper = sqlSession.getMapper(UserDao.class);
    User user = userMapper.findUserById(1,"lpf");
    Assert.assertNotNull("沒找到數據", user);
}
public interface UserDao {
    User findUserById (int id,String name);
}

1.若是useActualParamName設置爲true時

傳遞參數須要使用緩存

#{arg0}-#{argn}或者#{param1}-#{paramn}mybatis

好比:app

<select id="findUserById" resultType="com.lpf.entity.User" >
  select * from m_user where id = #{arg0}  and name =#{arg1}
</select>

或者ide

<select id="findUserById" resultType="com.lpf.entity.User" >
  select * from m_user where id = #{param1}  and name =#{param2}
</select>

2.若是useActualParamName設置爲false時

傳遞參數須要使用函數

#{0}-#{n}或者#{param1}-#{paramn}this

<select id="findUserById" resultType="com.lpf.entity.User" >
  select * from m_user where id = #{0}  and name =#{1}
</select>

或者spa

<select id="findUserById" resultType="com.lpf.entity.User" >
  select * from m_user where id = #{param1}  and name =#{param2}
</select>

下面是多個參數的錯誤寫法直接寫參數名(若是方法只有一個參數是能夠用參數名代替的,其實若是隻有一個參數,任何名稱都是能夠的)代理

<select id="findUserById" resultType="com.lpf.entity.User" >
  select * from m_user where id = #{id}  and name =#{name}
</select>

源碼解讀(3.4.6):

在mapper的代理對象調用方法時,最終會是MapperMethod對象的execute方法。以下:對象

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    // 若是目標方法是Object類繼承來的,直接調用目標方法
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  // 從緩存中獲取MapperMethod 對象,若是沒有就建立新的並添加
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  // 執行sql 語句
  return mapperMethod.execute(sqlSession, args);
}

MapperMethod的一個內部類MethodSignature封裝了Mapper接口中定義的方法的相關信息。而MethodSignature的一個屬性ParamNameResolver對象處理接口中定義的方法的參數列表。

ParamNameResolver 的屬性

// 記錄參數在參數列表中的位置索引與參數名稱之間的對應關係
private final SortedMap<Integer, String> names;

// 記錄對應的方法參數是否使用了@Param註解
private boolean hasParamAnnotation;

ParamNameResolver的構造函數

/**
 * 經過反射讀取方法中的信息,並初始化上面兩個字段
 * @param config
 * @param method
 */
public ParamNameResolver(Configuration config, Method method) {
  // 獲取參數列表中每一個參數的類型
  final Class<?>[] paramTypes = method.getParameterTypes();
  // 獲取參數列表上的註解  @Param
  final Annotation[][] paramAnnotations = method.getParameterAnnotations();
  // 該集合用於記錄參數索引與參數名稱的對應關係
  final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
  int paramCount = paramAnnotations.length;
  // 遍歷全部參數
  for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
    if (isSpecialParameter(paramTypes[paramIndex])) {
      // 若是參數是RowBounds類型或者ResultHandler類型,則跳過該參數
      continue;
    }
    String name = null;
    // 遍歷該參數上的註解集合
    for (Annotation annotation : paramAnnotations[paramIndex]) {
      if (annotation instanceof Param) {
        // 獲取@Param註解指定的參數名稱
        hasParamAnnotation = true;
        name = ((Param) annotation).value();
        break;
      }
    }
    // 沒有@Param註解的話 執行下面邏輯
    if (name == null) {
      // useActualParamName==true時  即name = arg0 ...
      if (config.isUseActualParamName()) {
        name = getActualParamName(method, paramIndex);
      }
      if (name == null) {//useActualParamName == false是  即 name="0" ...
        // use the parameter index as the name ("0", "1", ...)
        // 使用參數的索引做爲其名稱
        name = String.valueOf(map.size());
      }
    }
    map.put(paramIndex, name);
  }
  names = Collections.unmodifiableSortedMap(map);
}

names集合主要是在ParamNameResolver.getNamedParams方法中使用

/**
 *
 * @param args 用戶傳入的參數值列表
 * @return
 */
public Object getNamedParams(Object[] args) {
  final int paramCount = names.size();
  if (args == null || paramCount == 0) {
    return null;
  } else if (!hasParamAnnotation && paramCount == 1) {// 未使用@Param註解且參數列表只有一個
    return args[names.firstKey()];//即args[0] 參數的值
  } else {
    // 下面是爲參數建立param+索引的格式做爲默認參數名稱 如:param1  下標從1開始
    final Map<String, Object> param = new ParamMap<Object>();
    int i = 0;
    for (Map.Entry<Integer, String> entry : names.entrySet()) {
      param.put(entry.getValue(), args[entry.getKey()]);
      // add generic param names (param1, param2, ...)
      final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
      // ensure not to overwrite parameter named with @Param
      if (!names.containsValue(genericParamName)) {
        param.put(genericParamName, args[entry.getKey()]);
      }
      i++;
    }
    return param;
  }
}

總結:

1.若是接口方法有一個或多個參數,而且使用了@Param註解,sql語句中的參數用註解的value值,

2.若是接口方法的參數只有一個,而且沒有使用@Parma註解sql語句直接使用任何名稱都可。

3.若是接口的方法有多個參數,而且沒有使用@Parma註解,sql語句使用param1...paramn是不會錯的。

4.sql語句中的參數佔位符名稱和接口方法的參數名稱沒有什麼關係。

相關文章
相關標籤/搜索