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); }
傳遞參數須要使用緩存
#{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>
傳遞參數須要使用函數
#{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>
在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語句中的參數佔位符名稱和接口方法的參數名稱沒有什麼關係。