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语句中的参数占位符名称和接口方法的参数名称没有什么关系。