MyBatis-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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM