【Mybatis】Mapper接口的参数处理过程
下面是一个简单的Mapper接口调用,首先同个session的getMapper方法获取Mapper的代理对象,然后通过代理对象去调用Mapper接口的方法
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); Employee employee = mapper.getEmpByIdAndName(1,"tom");
Employee getEmpByIdAndName(@Param("id") Integer id, @Param("name") String name, int age);
源码分析:
首先看MapperProxy类,关键是mapperMethod.execute(this.sqlSession, args);
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } if (method.isDefault()) { return this.invokeDefaultMethod(proxy, method, args); } } catch (Throwable var5) { throw ExceptionUtil.unwrapThrowable(var5); } MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); }
MapperMethod类:execute方法中,在调用session操作之前,都会调用this.method.convertArgsToSqlCommandParam(args),最终还是调用ParamNameResolver类
public Object execute(SqlSession sqlSession, Object[] args) { Object result; Object param; switch(this.command.getType()) { case INSERT: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case UPDATE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); break; case DELETE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); break; case SELECT: if (this.method.returnsVoid() && this.method.hasResultHandler()) { this.executeWithResultHandler(sqlSession, args); result = null; } else if (this.method.returnsMany()) { result = this.executeForMany(sqlSession, args); } else if (this.method.returnsMap()) { result = this.executeForMap(sqlSession, args); } else if (this.method.returnsCursor()) { result = this.executeForCursor(sqlSession, args); } else { param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + this.command.getName()); } if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) { throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ")."); } else { return result; } }
public Object convertArgsToSqlCommandParam(Object[] args) {
return this.paramNameResolver.getNamedParams(args);
}
ParamNameResolver类, 参数会保存成这样的格式{id:1,name:Tom,2:30,param1:1,param2:Tom,param3:30}
public class ParamNameResolver {
private static final String GENERIC_NAME_PREFIX = "param"; private final SortedMap<Integer, String> names; private boolean hasParamAnnotation; // 构造器来初始化names,其值是{0=id, 1=name, 2=2} public ParamNameResolver(Configuration config, Method method) { Class<?>[] paramTypes = method.getParameterTypes(); Annotation[][] paramAnnotations = method.getParameterAnnotations(); SortedMap<Integer, String> map = new TreeMap(); int paramCount = paramAnnotations.length; for(int paramIndex = 0; paramIndex < paramCount; ++paramIndex) { if (!isSpecialParameter(paramTypes[paramIndex])) { String name = null; Annotation[] var9 = paramAnnotations[paramIndex]; int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { Annotation annotation = var9[var11]; if (annotation instanceof Param) { // 如果使用了param注解的参数,例如@Param("id") this.hasParamAnnotation = true; name = ((Param)annotation).value(); // name = "id"; break; } } if (name == null) { if (config.isUseActualParamName()) { // 如果配置文件中配置了useActualParamName=true
//允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) name = this.getActualParamName(method, paramIndex); // name=参数名称(id) } if (name == null) { name = String.valueOf(map.size()); // name=当前map的索引 name=2 } } map.put(paramIndex, name); } } this.names = Collections.unmodifiableSortedMap(map); } private String getActualParamName(Method method, int paramIndex) { return (String)ParamNameUtil.getParamNames(method).get(paramIndex); } private static boolean isSpecialParameter(Class<?> clazz) { return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz); } public String[] getNames() { return (String[])this.names.values().toArray(new String[0]); } public Object getNamedParams(Object[] args) { // args【1,"Tom",30】
// names:{0=id, 1=name, 2=2};构造器的时候就确定好了 int paramCount = this.names.size(); if (args != null && paramCount != 0) { if (!this.hasParamAnnotation && paramCount == 1) { // 如果只有一个元素,并且没有Param注解; return args[(Integer)this.names.firstKey()]; // 直接返回args[0] } else { // 多个元素或者有Param标注 Map<String, Object> param = new ParamMap(); int i = 0; for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) { Entry<Integer, String> entry = (Entry)var5.next();
//names集合的value作为key; names集合的key又作为取值的参考args[0]
//eg:{id=args[0]:1,name=args[1]:Tom,2=args[2]:30}
param.put((String)entry.getValue(), args[(Integer)entry.getKey()]);
//额外的将每一个参数也保存到map中,使用新的key:param1...paramN
//效果:有Param注解可以#{指定的key},或者#{param1}
String genericParamName = "param" + String.valueOf(i + 1); if (!this.names.containsValue(genericParamName)) { param.put(genericParamName, args[(Integer)entry.getKey()]); } } return param; } } else { return null; } } }