5MyBatis-parameterType封装Map流程
Posted 江湖小小白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5MyBatis-parameterType封装Map流程相关的知识,希望对你有一定的参考价值。
以如下入参为例,MyBatis 版本为 3.5.0
public MyUser selectMyUserIdAndAge(Integer id, @Param("user") MyUser user);
打上断点
大致流程
1、进入到 MapperProxy 类的 invoke 方法,执行接口的代理对象中的方法
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 代理以后,并不是所有 Mapper 的方法调用时都会调用这个 invoke 方法,如果这个方法是 Object 中通用的方法(toString、hashCode等)则无需执行 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { // 如果这个方法的权限修饰符是 public 并且是由接口提供,则执行 invokeDefaultMethod 方法,这里是由代理对象提供 return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } // 去缓存中找 MapperMethod final MapperMethod mapperMethod = cachedMapperMethod(method); // 执行 return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class .getDeclaredConstructor(Class.class, int.class); if (!constructor.isAccessible()) { constructor.setAccessible(true); } final Class<?> declaringClass = method.getDeclaringClass(); return constructor .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC) .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); } /** * Backport of java.lang.reflect.Method#isDefault() */ private boolean isDefaultMethod(Method method) { return (method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC && method.getDeclaringClass().isInterface(); } }
2、进入到 MapperMethod 类的 execute 方法,执行数据库操作
public class MapperMethod { private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; // 4种情况,insert|update|delete|select,分别调用 SqlSession 的 4 大类方法 switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { // 如果有结果处理器 executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { // 如果结果有多条记录 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { // 如果结果是map result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { // 如果结果是游标 result = executeForCursor(sqlSession, args); } else { // 否则就是一条记录 // 装配参数 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method \'" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; } ... }
3、进入到 ParamNameResolver 类的 getNamedParams 方法,进行参数封装
public class ParamNameResolver { private static final String GENERIC_NAME_PREFIX = "param"; private final SortedMap<Integer, String> names; private boolean hasParamAnnotation; public ParamNameResolver(Configuration config, Method method) { // 获取参数列表中每个参数的类型 final Class<?>[] paramTypes = method.getParameterTypes(); // 获取每个标了 @Param 注解的参数的值,赋值给 name final Annotation[][] paramAnnotations = method.getParameterAnnotations(); // 记录参数索引与参数名称的关系 final SortedMap<Integer, String> map = new TreeMap<>(); int paramCount = paramAnnotations.length; // 遍历参数保存至 map:(key:参数索引,value:name的值) for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { // 是否为特殊参数:RowBounds或ResultHandler if (isSpecialParameter(paramTypes[paramIndex])) { continue; } String name = null; // 标注了 @Param 注解:name的值为注解的值 for (Annotation annotation : paramAnnotations[paramIndex]) { if (annotation instanceof Param) { hasParamAnnotation = true; name = ((Param) annotation).value(); break; } } // 没有标 @Param 注解 if (name == null) { if (config.isUseActualParamName()) { // 全局配置:useActualParamName(jdk1.8):name为参数名 name = getActualParamName(method, paramIndex); } if (name == null) { // name为map.size():相当于当前元素的索引,0,1,2,3... name = String.valueOf(map.size()); } } map.put(paramIndex, name); } names = Collections.unmodifiableSortedMap(map); } private String getActualParamName(Method method, int paramIndex) { return ParamNameUtil.getParamNames(method).get(paramIndex); } private static boolean isSpecialParameter(Class<?> clazz) { return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz); } public String[] getNames() { return names.values().toArray(new String[0]); } public Object getNamedParams(Object[] args) { final int paramCount = names.size(); if (args == null || paramCount == 0) { // 没有参数 return null; } else if (!hasParamAnnotation && paramCount == 1) { // 未使用@Param注解且参数列表只有一个,即 args[0] 参数的值 return args[names.firstKey()]; } else { // 为参数创建 param+ 索引的格式作为默认参数名称 如:param1 下标从1开始 final Map<String, Object> param = new ParamMap<>(); int i = 0; // 遍历names集合;{0=arg0, 1=user} for (Map.Entry<Integer, String> entry : names.entrySet()) { // names集合的 value 作为 key; names 集合的 key 作为取值的参考args[0] // {arg0:args[0],user=args[1]} param.put(entry.getValue(), args[entry.getKey()]); final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1); if (!names.containsValue(genericParamName)) { // 额外的将每一个参数也保存到 param 中,使用新的key:param1...paramN,有 @Param 注解可以 #{指定的key},或者#{param1} param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } } }
https://my.oschina.net/u/3737136/blog/1811654
https://segmentfault.com/a/1190000015977801
https://github.com/tuguangquan/mybatis/tree/master/src/main/java/org/apache/ibatis/binding
以上是关于5MyBatis-parameterType封装Map流程的主要内容,如果未能解决你的问题,请参考以下文章