(四) mybatis 源码之 SQL 执行

执行 SQL

使用 SqlSession 提供的 API 也能执行 SQL 语句, 不过这种方使用的少, 还是习惯使用接口的方式

在这里插入图片描述

直接调用 SqlSession 的 getMapper 方法就可以为接口实现代理, 所以方法执行前会被拦截, 交给 MapperProxy 拦截, 它实现了 InterceptorHandler 接口

// 得到 MapperMethodInvoker 对象, 调用其 invoke 方法
// 最后交给 MapperMethod 执行, 查看他的 executor 方法
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 判断 SQL 语句类型 , DQL, DML
    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;
        }
        // DQL 语句单独处理
        case SELECT:
            // 方法无返回值且参数类型为  ResultHandler
            if (method.returnsVoid() && method.hasResultHandler()) {
                executeWithResultHandler(sqlSession, args);
                result = null;
            }
            // 是否返回一个集合
            else if (method.returnsMany()) {
                result = executeForMany(sqlSession, args);
            }
            // 是否返回一个 Map
            else if (method.returnsMap()) {
                result = executeForMap(sqlSession, args);
            }
            else if (method.returnsCursor()) {
                result = executeForCursor(sqlSession, args);
            }
            else {
                // 将方法的参数转换成 SQL 命令参数 (参数并没有填入 SQL)
                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;
}

SqlSession # selectOne

// 查看 SqlSession 的selectOne 方法, 发现它是调用了 selectList 方法
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 获取封装此 CRUD 的标签的 MappedStatement 对象 (通过 id 对应)
        MappedStatement ms = configuration.getMappedStatement(statement);
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    }
   ...
}
// 接着查看 Executor 的 query 方法
// 先是检查是否存在二级缓存, 如果不存在, 直接交给 SimpleExecutor 执行

Executor

四大对象之一

SqlSession 包含了一个 Executor 对象 (由 CacheExecutor 实现, 其实这个类也是一个包装 , 它也包含了一个 Executor (默认由 SimpleExecutor 实现))

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 绑定 SQL, 获取到方法参数的映射关系, 参数的元数据, 放到 BoundSql 对象中
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 二级缓存的 Key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

SimpleExecutor

先是调用父类 BaseExecutor 的 query 方法, 检查一级缓存中是否存在需要的结果, 如不存在, 将 SQL 的结果放入一级缓存中

// 查看 BaseExecutor 的 queryFromDatabase 方法
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);// 这个常量充当占位符
    try {
        // 执行 SQL
        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    }
    finally {
        localCache.removeObject(key);
    }
    // 将结果放入缓存
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}
// 调用了 SimpleExecutor 的 doQuery 方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        // 创建 SQL 执行器, 同时创建了 ParameterHandler, ResultSetHandler 对象
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        // 从数据源中获取连接, 并发送 SQL 语句, 根据 Statement 的类型进行处理
        stmt = prepareStatement(handler, ms.getStatementLog());
        // 把参数填充到 SQL 中, 执行 SQL
        return handler.query(stmt, resultHandler);
    }
    finally {
        closeStatement(stmt);
    }
}

先查看这个 Configuration 的 newStatementHandler 方法

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 同时在里面创建了 ParameterHandler , ResultHandler 对象
    // 一个 StatementHandler 对象包含了 ParameterHandler , ResultHandler 对象
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 拦截器, 应用插件
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}
// 接着看 RoutingStatementHandler 的构造方法
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 判断此 MappedStatement 的 statemenType
    switch (ms.getStatementType()) {
        case STATEMENT:
            // 相当于 JDBC 的 Statement
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            // 相当于 JDBC 的 PreparedStatement
            // 在 PreparedStatementHandler 的构造器中创建了 
            // ParameterHandler 和 PreparedStatementHandler 对象
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            // 用于调用存储过程的 Statement
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

}

可以发现, 这里的 PreparedStatementHandler, ParameterHandler, PreparedStatementHandler 在创建完之后, 都应用到了拦截器, 这里就可以对 mybatis 进行插件开发

接着查看这个 prepareStatement 方法

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取连接, 生成代理对象, ConnectionLogger (mybatis 最新版本3.5.5)
    Connection connection = getConnection(statementLog);
    // 发送 SQL 语句, 预处理, 同样为 Statement 生成代理对象 PreparedStatementLogger
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 交给 ParameterHandler 处理, 设置预处理参数
    handler.parameterize(stmt);
    return stmt;
}
------------------------
// 接着看 StatementHandler 的 parameterize 方法
// 发现是 ParameterHandler 处理, 为 SQL 设置预编译参数
// 查看 ParameterHandler 的 setParameters 方法
public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                // 解析参数值 (需要填入 SQL), JDBC 类型
                ....
                try {
                    // 交给 TypeHandler 处理, 给 SQL 预处理, 设置参数
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                }
                catch (TypeException | SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

StatementHandler

实现类如下 :

在这里插入图片描述

实现类为 RoutingStatementHandler , 类似于 CacheExecutor , 也是一个包装, 里面包含了一个 StatementHandler 对象 (默认由 PreparedStatementHandler 实现)

// 查看 PreparedStatementHandler 的 query 方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {

    PreparedStatement ps = (PreparedStatement) statement;
    // 被代理对象拦截, 执行 SQL 语句, 将结果集保存在 PreparedStatement 对象中
    ps.execute();
    // 结果集映射
    return resultSetHandler.handleResultSets(ps);
}

ResultSetHandler 默认由 DefaultSetHandler 实现, 查看他的 handleResultSets 方法

public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    // 保存查询的结果集
    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    // 先处理 resultSet , 包装一下, 存放了数据库中的所有字段, 字段的 Java 类型, JDBC 类型 (mybatis定义的)
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    // 从 MappedStatement 对象中获取到 resultMap
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    // 验证 resultMap 的数量
    validateResultMapsCount(rsw, resultMapCount);
    // 将 resultMap 的结果与 POJO 的属性映射
    while (rsw != null && resultMapCount > resultSetCount) {
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 处理 ResultSet, 放入 multipleResults 集合中
        handleResultSet(rsw, resultMap, multipleResults, null);
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
    }

    .....

    return collapseSingleResultList(multipleResults);
}

查看这个 handleResultSet 方法

// 一路调用, 来到 DefaultResultSetHandler 的 handleRowValuesForSimpleResultMap
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
    throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
        // 封装成 POJO, 匹配的构造器, 否者使用 setter 方法为属性填充属性
        Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
        // 将封装好的 POJO 存放到 ResultHandler 对象中的 List 集合中
        storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
}

最后处理这个 multipleResults 集合, 将结果返回出去, 先将其放入一级缓存, 如果存在二级缓存就会放入二级缓存

至于 SQL 的执行细节, 下篇继续

猜你喜欢

转载自blog.csdn.net/Gp_2512212842/article/details/107653627