【Mybatis】Mybatis源码之MapperMethod#executeForMany方法流程

770 阅读2分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

时序图

sequenceDiagram
participant A as MapperMethod
participant B as MethodSignature
participant C as ParamNameResolver
participant D as DefaultSqlSession

A ->> A : executeForMany
A ->> B : convertArgsToSqlCommandParam
B ->> C : getNamedParams
C -->> A : Object param
C ->> D : selectList
D -->> C : List<E>
C -->> A : Object result

详细步骤

MapperMethod#executeForMany

/**
 * 查询多个结果
 * @param sqlSession session
 * @param args 当前查询的实际参数
 * @param <E>
 * @return
 */
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    // 参数名称与参数值的映射
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {// 判断是否有RowBounds参数
        // 获取RowBounds参数
        RowBounds rowBounds = method.extractRowBounds(args);
        // 带RowBounds的查询
        result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
        // 不带RowBounds的查询
        result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
        if (method.getReturnType().isArray()) {
            return convertToArray(result);
        } else {
            return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
        }
    }
    return result;
}

ParamNameResolver#getNamedParams

/**
 * <p>
 * A single non-special parameter is returned without a name.
 * Multiple parameters are named using the naming rule.
 * In addition to the default names, this method also adds the generic names (param1, param2,
 * ...).
 * </p>
 * <p>
 * 将被解析的方法中的参数名称列表与传入的`Object[] args`进行对应,返回对应关系。
 * <p>
 * 如果只有一个参数,直接返回参数
 * 如果有多个参数,则进行与之前解析出的参数名称进行对应,返回对应关系
 *
 * 参数名称:参数值
 * paramx:参数值
 */
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()];
    } else {
        final Map<String, Object> param = new ParamMap<>();
        int i = 0;
        for (Map.Entry<Integer, String> entry : names.entrySet()) {
            // 首先按照类注释中提供的key,存入一遍   【参数的@Param名称 或者 参数排序:实参值】
            // 注意,key和value交换了位置
            param.put(entry.getValue(), args[entry.getKey()]);

            // 再按照param1, param2, ...的命名方式存入一遍
            // add generic param names (param1, param2, ...)
            final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
            // ensure not to overwrite parameter named with @Param
            // 生成的参数名称不能覆盖names中已有的参数名称
            if (!names.containsValue(genericParamName)) {
                param.put(genericParamName, args[entry.getKey()]);
            }
            i++;
        }
        return param;
    }
}

DefaultSqlSession#selectList

/**
 * 查询结果列表
 * @param <E> 返回的列表元素的类型
 * @param statement SQL语句
 * @param parameter 参数对象
 * @param rowBounds  翻页限制条件
 * @return 结果对象列表
 */
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        /**
         * 获取查询语句
         * 设置MappedStatement映射,见{@link XMLStatementBuilder#parseStatementNode()}
         */
        MappedStatement ms = configuration.getMappedStatement(statement);
        /**
         * 交由执行器进行查询,由于全局配置cacheEnabled默认是打开的,因此此处的executor通常都是CachingExecutor
         * 获取executor,见{@link Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)}
         */
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

以上便是Mybatis中MapperMethod#executeForMany方法的流程。