BaseExecutor.query()方法解析

2021-03-13  本文已影响0人  布拉德老瓜

BaseExecutor.query(ms, ... , boundSql)方法执行流程

query()

它的逻辑很简单:

queryFromeDataBase(ms, param, ... ,cacheKey, boundSql)

这个方法目前没什么好说的,且看doQuery(...)方法吧

    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

        List list;
        try {
            //todo:重点关注
            list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            this.localCache.removeObject(key);
        }
        // 放到缓存
        this.localCache.putObject(key, list);
        // CALLABLE的sql语句, 作为存放到参数缓存。也就是说,该语句的执行结果可以作为参数,被其他的语句使用。
        if (ms.getStatementType() == StatementType.CALLABLE) {
            this.localOutputParameterCache.putObject(key, parameter);
        }

        return list;
    }

queryFromeDataBase(ms, ... , boundSql)的核心在于doQuery(ms, ... , boundSql),重点来了。

可以看到这个方法的就做了四件事:

  1. 创建StatementHandler
  2. 通过statementHandler解析出statement
  3. 利用statementHandler完成查询
  4. closeStatement.
    其主体部分都离不开StatementHandler, 因此可以说,搞明白了StatementHandler , 也就搞明白了doQuery.我们知道StatementHandler、ParameterHandler、ResultSetHandler和Executor是mybatis的四大核心对象,所以在这里应当留心StatementHandler是如何被创建的、内部有哪些重要属性,以及它有哪些作用。
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }

1. 方法创建statementHandler时的参数包含了哪些信息,可以让我们与数据库交互并查到数据

    configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql)
debug: this

总之,数据库连接,事务控制,执行sql所需的语句,sql中参数的位置、名称和取值信息现在都有了。

2. StatementHandler的创建过程

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }

创建RoutingStatementhandler

判断当前statement的类类型,调用对应的构造方法,将结果作为当前RoutingStatementhandler的代理。此后实际干活的都是这个delegate.

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        switch(ms.getStatementType()) {
        case STATEMENT:
            this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    }

这三种statementHandler从构造方法上来讲都是一样的,只是最终创建出来的handler类型不一样。

public class PreparedStatementHandler extends BaseStatementHandler {
    public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
    }
...
}

statementHandler的构造方法都是调父类构造方法super(xxxxx)。

    protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
        this.objectFactory = this.configuration.getObjectFactory();
        if (boundSql == null) {
            this.generateKeys(parameterObject);
            boundSql = mappedStatement.getBoundSql(parameterObject);
        }

        this.boundSql = boundSql;
        this.parameterHandler = this.configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = this.configuration.newResultSetHandler(executor, mappedStatement, rowBounds, this.parameterHandler, resultHandler, boundSql);
    }

创建该对象过程中重要的点在于parameterHandler和resultSetHandler.(他们也属于四个核心对象,这下核心对象就全部创建完了)。ParameterHandler负责为 PreparedStatement 的 sql 语句参数动态赋值。 ResultSetHandler负责处理两件事:(1)处理Statement执行后产生的结果集,生成结果列表(2)处理存储过程执行后的输出参数。关于这两个组件,等有时间另外拧出来讲。

//pluginAll伪代码
foreach interceptor in chain{
  target = interceptor.plugin(target);
}
return target;

interceptor.plugin(target)创建代理对象

    // interceptor.plugin(target)
    default Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public static Object wrap(Object target, Interceptor interceptor) {
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
    }

2. Statement的创建过程

重新post一份doQuery代码, 现在我们完成了StatementHandler的创建。

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
            // here we are
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // the next thing to do
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }

接下来就要解析sql语句了,之前的sql语句信息在boundSql中。我们来看看如何解析吧。

    //executor.prepareStatement()
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        // 获取连接对象
        Connection connection = this.getConnection(statementLog);
        // 创建statement对象 
        Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
        // statement对象参数化
        handler.parameterize(stmt);
        return stmt;
    }

executor.getConnection()

    protected Connection getConnection(Log statementLog) throws SQLException {
        Connection connection = this.transaction.getConnection();
        return statementLog.isDebugEnabled() ? ConnectionLogger.newInstance(connection, statementLog, this.queryStack) : connection;
    }

transaction.getConnection(): 如果connection已经创建直接返回该对象,没创建则利用dataSource去创建一个,然后设置隔离级别和是否自动提交。

    public Connection getConnection() throws SQLException {
        if (this.connection == null) {
            this.openConnection();
        }

        return this.connection;
    }

    protected void openConnection() throws SQLException {
        if (log.isDebugEnabled()) {
            log.debug("Opening JDBC Connection");
        }

        this.connection = this.dataSource.getConnection();
        if (this.level != null) {
            this.connection.setTransactionIsolation(this.level.getLevel());
        }

        this.setDesiredAutoCommit(this.autoCommit);
    }
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(this.boundSql.getSql());
        Statement statement = null;

        try {
             // Look here
            statement = this.instantiateStatement(connection);
            this.setStatementTimeout(statement, transactionTimeout);
            this.setFetchSize(statement);
            return statement;
        } catch (SQLException var5) {
            this.closeStatement(statement);
            throw var5;
        } catch (Exception var6) {
            this.closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + var6, var6);
        }
    }

    // BaseStatementHandler中的该方法为抽象方法,由其子类实现
    protected abstract Statement instantiateStatement(Connection var1) throws SQLException;

BaseStatementHandler中的instantiateStatement为抽象方法,咱现在用的是他的子类PreparedStatementHandler,来看看是如何实现的吧。

    //PreparedStatementHandler.instantiateStatement(connection)
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = this.boundSql.getSql();
        if (this.mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
            String[] keyColumnNames = this.mappedStatement.getKeyColumns();
            return keyColumnNames == null ? connection.prepareStatement(sql, 1) : connection.prepareStatement(sql, keyColumnNames);
        } else {
            return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.prepareStatement(sql) : connection.prepareStatement(sql, this.mappedStatement.getResultSetType().getValue(), 1007);
        }
    }

从boundSql中获取其sql属性,然后connection.prepareStatement(...)

    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(this.boundSql.getSql());
        Statement statement = null;

        try {
            statement = this.instantiateStatement(connection);
            this.setStatementTimeout(statement, transactionTimeout);
            this.setFetchSize(statement);
            return statement;
        } catch (SQLException var5) {
            this.closeStatement(statement);
            throw var5;
        } catch (Exception var6) {
            this.closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + var6, var6);
        }
    }

经过这一步操作,statement对象就实例化了,有了数据库连接的connection和sql语句信息,但是sql语句还是没有参数化。

    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for(int i = 0; i < parameterMappings.size(); ++i) {
                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    if (this.boundSql.hasAdditionalParameter(propertyName)) {
                        value = this.boundSql.getAdditionalParameter(propertyName);
                    } else if (this.parameterObject == null) {
                        value = null;
                    } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
                        value = this.parameterObject;
                    } else {
                        MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
                        value = metaObject.getValue(propertyName);
                    }

                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = this.configuration.getJdbcTypeForNull();
                    }

                    try {
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (SQLException | TypeException var10) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
                    }
                }
            }
        }

参数化的过程简单来说就是遍历parameterMappings数组,数组中第i个元素对应的就是sql语句中第i个参数的信息(不包含参数值),从中获取property(即参数名)。再根据参数名去boundSql的parameterObject(本质是一个map)中获取该参数名对应的参数值。最后,将该参数值拼接到sql语句后面的对应位置。参数化完成之后的statement:

com.mysql.cj.jdbc.ClientPreparedStatement: select * from article_share_info where article_id = 2

查询过程handler.query(stmt, resultHandler)

将statement转为preparedStatement, 再由它执行,最后由ResultSetHandler完成结果集的封装。

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        //转ps
        PreparedStatement ps = (PreparedStatement)statement;
        //执行sql语句并将结果以字节数组的形式存放在ps.results中
        ps.execute();
        // ResultSetHandler解析ps内的结果集,封装结果集为指定的resultMap类型并将其返回
        return this.resultSetHandler.handleResultSets(ps);
    }
上一篇 下一篇

猜你喜欢

热点阅读