Mybatis随笔

Mybatis随笔(九) StatementHandler解析

2020-03-29  本文已影响0人  sunyelw

Mybatis从SqlSession到Executor再到Statement,这就是一条SQL执行的调用过程,而Statement接口就是数据库底层的统一对外接口,不同数据库厂商自定义的驱动中就包括实现这个接口。

而Mybatis并没有直接从Executor访问Statement,中间还套了一层StatementHandler。


1、StatementHandler概览

StatementHandler

跟Executor的继承实现很像,都有一个Base,Base下面又有几个具体实现子类,这又是模板模式的应用。看下另一个Routing就不同于CacheExecutor用于二级缓存之类的实际作用了,仅用于维护三个Base子类的创建与调用。

看下BaseStatementHandler的属性

// 1.配置
protected final Configuration configuration;
// 2.对象处理工厂
protected final ObjectFactory objectFactory;
// 3.类型处理
protected final TypeHandlerRegistry typeHandlerRegistry;
// 4.结果集处理
protected final ResultSetHandler resultSetHandler;
// 5.参数处理
protected final ParameterHandler parameterHandler;

// 6.具体执行器
protected final Executor executor;
// 7.具体SQL映射对象
protected final MappedStatement mappedStatement;
// 8.执行SQL条数参数
protected final RowBounds rowBounds;

// 9.具体SQL信息
protected BoundSql boundSql;

2、StatementHandler解析

因为涉及到了具体操作数据库,就是由三个BaseExecutor的子类自己实现,不过三个的逻辑大体一致,分几个步骤

2.1 创建StatementHandler
@Override
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();
        // here
        // here
        // here
        // 创建 StatementHandler
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = prepareStatement(handler, ms.getStatementLog());
    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) {
    // 1.创建StatementHandler
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 2.插件处理
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}

看下 RoutingStatementHandler 的构造方法是如何创建StatementHandler的

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

嗯,很眼熟的策略模式,按照statementType的值来决定返回哪种StatementHandler。

statementType的取值?构造方法中默认取值为PREPARED

mappedStatement.statementType = StatementType.PREPARED;

可以通过<select />的statementType属性指定

<select id="getAll" resultType="Student2" statementType="CALLABLE">
    SELECT * FROM Student
</select>

或通过@SelectKey的statementType属性指定

@SelectKey(keyProperty = "account", 
        before = false, 
        statementType = StatementType.STATEMENT, 
        statement = "select * from account where id = #{id}", 
        resultType = Account.class)
Account selectByPrimaryKey(@Param("id") Integer id);
2.2 创建Statement

然后就是创建并处理Statement,用于与JDBC交互了,三个Executor的逻辑基本一致,分三步

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 1.拿到连接
    Connection connection = getConnection(statementLog);
    // 2.创建Statement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 3.预编译
    handler.parameterize(stmt);
    return stmt;
}

然后是SimpleExecutor、ReuseExecutor就是直接执行了,而BatchExecutor就得存起来等一次批量执行的调用了。

BaseStatementHandler抽象类只有一个抽象方法 instantiateStatement 方法,是在预编译的时候调用,用于获取Statement对象,由子类实现

protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

SimpleStatementHandler、CallableStatementHandler比较简单直接获取

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
        return connection.createStatement();
    } else {
        return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
}

而PrepareStatementHandler则比较复杂

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
        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) {
        return connection.prepareStatement(sql);
    } else {
        return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
}

下面以查询为例简单看下三个Statement的区别。

2.3 SimpleStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.handleResultSets(statement);
}

对于SimpleStatementHandler来说,就是简单执行SQL后,将结果集转化成list返回。

2.4 PreparedStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
}

将Statement转化为PreparedStatement后执行,将结果集转化成list返回。

2.5 CallableStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    CallableStatement cs = (CallableStatement) statement;
    cs.execute();
    List<E> resultList = resultSetHandler.handleResultSets(cs);
    resultSetHandler.handleOutputParameters(cs);
    return resultList;
}

将Statement转化为CallableStatement后进行操作,处理存储过程的输出并将结果集转化成list返回。

2.6 ResultSetHandler

简单看下结果集处理接口ResultSetHandler

public interface ResultSetHandler {

    // 将结果集转化成list
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;

    // 将结果集转化出呢个cursor
    <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

    // 处理存储过程的输出
    void handleOutputParameters(CallableStatement cs) throws SQLException;
}

这个接口只有一个默认实现DefaultResultSetHandler,这个类的方法着实有点多。。


DefaultResultSetHandler-method

嗯,有需要再来深究。

总结

  1. Mybatis通过StatementHandler来处理与JDBC的交互
  2. Statement是具体与数据库底层打交道的接口,不同数据库厂商需要自己定义实现,比如MySQL、Oracle等
上一篇 下一篇

猜你喜欢

热点阅读