【Mybatis】Mybatis源码之获取真实执行的SQL

571 阅读1分钟

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

时序图

sequenceDiagram
participant A as CachingExecutor
participant B as BaseExecutor
participant C as SimpleExecutor
participant D as PreparedStatementHandler
participant E as DefaultParameterHandler
participant F as BaseTypeHandler
participant G as UnknownTypeHandler

A ->> B : query
B ->> C : doQuery
C ->> D : parameterize
D ->> E : setParameters
E ->> F : setParameter
F ->> G : setNonNullParameter

详细步骤

SimpleExecutor#doQuery

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        // 获取MappedStatement中的Configuration对象
        Configuration configuration = ms.getConfiguration();
        // 获取StatementHandler,默认是PreparedStatementHandler,并使用RoutingStatementHandler进行包装
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        // 获取编译后的Statement对象
        stmt = prepareStatement(handler, ms.getStatementLog());
        // 执行查询
        return handler.query(stmt, resultHandler);
    } finally {
        // 关闭Statement资源
        closeStatement(stmt);
    }
}

SimpleExecutor#prepareStatement

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取代理的Connection对象
    Connection connection = getConnection(statementLog);
    // StatementHandler对Statement进行初始化,并设置超时时间及fetchSize属性
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 将SQL中的?替换为参数值
    handler.parameterize(stmt);
    return stmt;
}

PreparedStatementHandler#instantiateStatement

  • 初始化Statement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
        // 使用了useGeneratedKeys属性
        String[] keyColumnNames = mappedStatement.getKeyColumns();
        if (keyColumnNames == null) {
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
        } else {
            return connection.prepareStatement(sql, keyColumnNames);
        }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
        // 未使用useGeneratedKeys属性
        return connection.prepareStatement(sql);
    } else {
        // 设置结果集只读
        return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
}

DefaultParameterHandler#setParameters

/**
 * 为语句设置参数,
 * setParameters方法的实现逻辑也很简单,就是依次取出每个参数的值,然后根据参数类型调用PreparedStatement中的赋值方法完成赋值。
 * @param ps 语句
 */
@Override
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);
            // ParameterMode.OUT是 CallableStatement的输出参数,已经单独注册,故忽略
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                // 取出参数名称
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                    // 从附加参数中读取属性
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    // 参数对象是基本类型,则参数对象即为参数值
                    value = parameterObject;
                } else {
                    // 参数对象是复杂类型,取出参数对象的该属性值
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                // 确定该参数的处理器
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    // 当存在具体的TypeHandler时,直接调用方法进行赋值,而当TypeHandler为UnknownTypeHandler类型时,则根据传入参数的类型来确定具体的TypeHandler
                    // 此方法最终根据参数类型,调用java.sql.PreparedStatement类中的参数赋值方法,对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);
                }
            }
        }
    }
}

UnknownTypeHandler#setNonNullParameter

@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
        throws SQLException {
    // 根据传入的参数值,以及JdbcType来确定具体的TypeHandler
    TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
    // 使用确定的TypeHandler对SQL进行赋值
    handler.setParameter(ps, i, parameter, jdbcType);
}

private TypeHandler<?> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
    TypeHandler<?> handler;
    if (parameter == null) {
        // 参数为空
        handler = OBJECT_TYPE_HANDLER;
    } else {
        // 根据参数值的类型及jdbcType确定具体的TypeHandler
        handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType);
        // check if handler is null (issue #270)
        if (handler == null || handler instanceof UnknownTypeHandler) {
            handler = OBJECT_TYPE_HANDLER;
        }
    }
    return handler;
}

以上便是Mybatis中通过ParameterHandler获取真实执行的SQL流程。