【Mybatis】Mybatis源码之DefaultSqlSession#selectList方法流程

562 阅读2分钟

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

时序图

sequenceDiagram
participant A as DefaultSqlSession
participant B as CachingExecutor
participant C as MappedStatement
participant D as SqlSource
participant E as Executor

A ->> B : query
B ->> C : getBoundSql
C ->> D : getBoundSql
D -->> C : BoundSql
C -->> B : BoundSql
B ->> B : createCacheKey
B ->> E : createCacheKey
E -->> B : CacheKey
B ->> B : query
B ->> E : query
E -->> B : List<E>

详细步骤

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();
    }
}

CachingExecutor#query

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 获取BoundSql,其中包含可执行的SQL以及响应的参数映射
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 使用实际Executor生成缓存键
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    // 使用实际Executor执行查询
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

MappedStatement#getBoundSql

public BoundSql getBoundSql(Object parameterObject) {
    // 获取可执行的SQL
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    // 获取参数映射
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    // 判断参数映射是否为空,如果为空,则使用默认的参数映射(配置的parameterMap或parameterType)
    if (parameterMappings == null || parameterMappings.isEmpty()) {
        boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    // 检查内嵌结果集映射
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
        String rmId = pm.getResultMapId();
        if (rmId != null) {
            ResultMap rm = configuration.getResultMap(rmId);
            if (rm != null) {
                hasNestedResultMaps |= rm.hasNestedResultMaps();
            }
        }
    }

    return boundSql;
}

CachingExecutor#query

  • 如果有二级缓存,则通过缓存进行查询,如果缓存中不存在,则查询后将结果进行缓存。
  • 二级缓存解析位置,见Mybatis源码之mappers标签解析
/**
 * 查询数据库中的数据
 *
 * @param ms              映射语句
 * @param parameterObject 参数对象
 * @param rowBounds       翻页限制条件
 * @param resultHandler   结果处理器
 * @param key             缓存的键
 * @param boundSql        查询语句
 * @param <E>             结果类型
 * @return 结果列表
 * @throws SQLException
 */
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
        throws SQLException {
    // 获取MappedStatement对应的缓存,可能的结果有:该命名空间的缓存、共享的其它命名空间的缓存、无缓存
    Cache cache = ms.getCache();
    // 如果映射文件未设置<cache>或<cache-ref>则,此处cache变量为null
    if (cache != null) { // 存在缓存
        // 根据要求判断语句执行前是否要清除二级缓存,如果需要,清除二级缓存
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) { // 该语句使用缓存且没有结果处理器
            // 二级缓存不支持含有输出参数的CALLABLE语句,故在这里进行判断
            ensureNoOutParams(ms, boundSql);
            @SuppressWarnings("unchecked")
            // 从缓存中读取结果
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) { // 缓存中没有结果
                // 交给被包装的执行器执行
                list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                // 缓存被包装执行器返回的结果
                tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
        }
    }
    // 交由被包装的实际执行器执行
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

以上便是Mybatis中DefaultSqlSession#selectList方法的流程。