code-learning/mybatis/22-mybatis-SQL 执行(二)之 StatementHandler.md

24 KiB
Raw Permalink Blame History

精尽 MyBatis 源码分析 —— SQL 执行(二)之 StatementHandler

1. 概述

本文,我们来分享 SQL 执行的第二部分,statement 包。整体类图如下:![类图](22-mybatis-SQL 执行(二)之 StatementHandler.assets/01.png)类图

  • 我们可以看到,整体是以 StatementHandler 为核心。所以,本文主要会看到的就是 StatementHandler 对 JDBC Statement 的各种操作。

而 StatementHandler 在整个 SQL 执行过程中,所处的位置如下:![整体流程](22-mybatis-SQL 执行(二)之 StatementHandler.assets/02.png)整体流程

2. StatementHandler

org.apache.ibatis.executor.statement.StatementHandler Statement 处理器,其中 Statement 包含 java.sql.Statementjava.sql.PreparedStatementjava.sql.CallableStatement 三种。代码如下:

// StatementHandler.java

public interface StatementHandler {

    /**
     * 准备操作,可以理解成创建 Statement 对象
     *
     * @param connection         Connection 对象
     * @param transactionTimeout 事务超时时间
     * @return Statement 对象
     */
    Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;

    /**
     * 设置 Statement 对象的参数
     *
     * @param statement Statement 对象
     */
    void parameterize(Statement statement) throws SQLException;
    
    /**
     * 添加 Statement 对象的批量操作
     *
     * @param statement Statement 对象
     */
    void batch(Statement statement) throws SQLException;
    /**
     * 执行写操作
     *
     * @param statement Statement 对象
     * @return 影响的条数
     */
    int update(Statement statement) throws SQLException;
    /**
     * 执行读操作
     *
     * @param statement Statement 对象
     * @param resultHandler ResultHandler 对象,处理结果
     * @param <E> 泛型
     * @return 读取的结果
     */
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
    /**
     * 执行读操作,返回 Cursor 对象
     *
     * @param statement Statement 对象
     * @param <E> 泛型
     * @return Cursor 对象
     */
    <E> Cursor<E> queryCursor(Statement statement) throws SQLException;

    /**
     * @return BoundSql 对象
     */
    BoundSql getBoundSql();
    /**
     * @return ParameterHandler 对象
     */
    ParameterHandler getParameterHandler();

}
  • 比较简单,胖友愁一愁。

StatementHandler 有多个子类,如下图所示:类图类图

  • 左边的三个实现类,分别对应 java.sql.Statementjava.sql.PreparedStatementjava.sql.CallableStatement 三种不同的实现类。
  • 右边的 RoutingStatementHandler 实现类,负责将不同的 Statement 类型,路由到上述三个实现类上。

下面,我们先看右边的实现类,再看左边的实现类。

3. RoutingStatementHandler

org.apache.ibatis.executor.statement.RoutingStatementHandler ,实现 StatementHandler 接口,路由的 StatementHandler 对象,根据 Statement 类型,转发到对应的 StatementHandler 实现类中。

3.1 构造方法

// RoutingStatementHandler.java

/**
 * 被委托的 StatementHandler 对象
 */
private final StatementHandler delegate;

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 根据不同的类型,创建对应的 StatementHandler 实现类
    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());
    }
}
  • 根据不同的类型,创建对应的 StatementHandler 实现类。
  • 😈 经典的装饰器模式。实际上,有点多余。。。还不如改成工厂模式。

3.2 实现方法

所有的实现方法,调用 delegate 对应的方法即可。代码如下:

// RoutingStatementHandler.java

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
}

@Override
public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
}

@Override
public void batch(Statement statement) throws SQLException {
    delegate.batch(statement);
}

@Override
public int update(Statement statement) throws SQLException {
    return delegate.update(statement);
}

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
}

@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    return delegate.queryCursor(statement);
}

@Override
public BoundSql getBoundSql() {
    return delegate.getBoundSql();
}

@Override
public ParameterHandler getParameterHandler() {
    return delegate.getParameterHandler();
}
  • 是不是更加觉得,换成工厂模式更合适。

4. BaseStatementHandler

org.apache.ibatis.executor.statement.BaseStatementHandler ,实现 StatementHandler 接口StatementHandler 基类,提供骨架方法,从而使子类只要实现指定的几个抽象方法即可。

4.1 构造方法

// BaseStatementHandler.java

protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;
protected final ParameterHandler parameterHandler;

protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;

protected BoundSql boundSql;

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 获得 Configuration 对象
    this.configuration = mappedStatement.getConfiguration();

    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    // 获得 TypeHandlerRegistry 和 ObjectFactory 对象
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    // <1> 如果 boundSql 为空一般是写类操作例如insert、update、delete ,则先获得自增主键,然后再创建 BoundSql 对象
    if (boundSql == null) { // issue #435, get the key before calculating the statement
        // <1.1> 获得自增主键
        generateKeys(parameterObject);
        // <1.2> 创建 BoundSql 对象
        boundSql = mappedStatement.getBoundSql(parameterObject);
    }
    this.boundSql = boundSql;

    // <2> 创建 ParameterHandler 对象
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    // <3> 创建 ResultSetHandler 对象
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
  • 大体是比较好理解的,我们就看几个点。

  • <1> 处,如果 boundSql 为空:

    • 一般是类操作,例如:insertupdatedelete 。代码如下:

      // SimpleExecutor.java
      
      @Override
      public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
          Statement stmt = null;
          try {
              Configuration configuration = ms.getConfiguration();
              // <x> 创建 StatementHandler 对象
              StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
              // 初始化 StatementHandler 对象
              stmt = prepareStatement(handler, ms.getStatementLog());
              // 执行 StatementHandler ,进行写操作
              return handler.update(stmt);
          } finally {
              // 关闭 StatementHandler 对象
              closeStatement(stmt);
          }
      }
      
      • <x> 处,调用 Configuration#newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 方法,创建 StatementHandler 对象。其中,方法参数 boundSqlnull
    • <1.1> 处,调用 #generateKeys(Object parameter) 方法,获得自增主键。代码如下:

      // BaseStatementHandler.java
      
      protected void generateKeys(Object parameter) {
          // 获得 KeyGenerator 对象
          KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
          ErrorContext.instance().store();
          // 前置处理,创建自增编号到 parameter 中
          keyGenerator.processBefore(executor, mappedStatement, null, parameter);
          ErrorContext.instance().recall();
      }
      
      • 通过 KeyGenerator 对象,创建自增编号到 parameter 中。😈 详细的解析,见后续的 KeyGenerator 的内容。
    • <1.2> 处,调用 MappedStatement#getBoundSql(Object parameterObject) 方法,创建 BoundSql 对象。

    • 这个流程,可以调试下 BindingTest#shouldInsertAuthorWithSelectKeyAndDynamicParams() 单元测试方法。

  • <2> 处,调用 Configuration#newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) 方法,创建 ParameterHandler 对象。代码如下:

    // Configuration.java
    
    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        // 创建 ParameterHandler 对象
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        // 应用插件
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
    }
    
    // XMLLanguageDriver.java
    
    @Override
    public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        // 创建 DefaultParameterHandler 对象
        return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
    }
    
  • <3> 处,创建 ResultSetHandler 对象。详细解析,见 《精尽 MyBatis 源码分析 —— SQL 执行(四)之 ResultSetHandler》

4.2 prepare

// BaseStatementHandler.java

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
        // <1> 创建 Statement 对象
        statement = instantiateStatement(connection);
        // 设置超时时间
        setStatementTimeout(statement, transactionTimeout);
        // 设置 fetchSize
        setFetchSize(statement);
        return statement;
    } catch (SQLException e) {
        // 发生异常,进行关闭
        closeStatement(statement);
        throw e;
    } catch (Exception e) {
        // 发生异常,进行关闭
        closeStatement(statement);
        throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
}
  • <1> 处,创建 #instantiateStatement(Connection connection) 方法,创建 Statement 对象。这是一个抽象方法,需要子类去实现。代码如下:

    // BaseStatementHandler.java
    
    protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
    
  • <2> 处,调用 #setStatementTimeout(Statement stmt, Integer transactionTimeout) 方法,设置超时时间。代码如下:

    // BaseStatementHandler.java
    
    protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
        // 获得 queryTimeout
        Integer queryTimeout = null;
        if (mappedStatement.getTimeout() != null) {
            queryTimeout = mappedStatement.getTimeout();
        } else if (configuration.getDefaultStatementTimeout() != null) {
            queryTimeout = configuration.getDefaultStatementTimeout();
        }
        // 设置查询超时时间
        if (queryTimeout != null) {
            stmt.setQueryTimeout(queryTimeout);
        }
        // 设置事务超时时间
        StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);
    }
    
  • <3> 处,设置 fetchSize 。代码如下:

    // BaseStatementHandler.java
    
    protected void setFetchSize(Statement stmt) throws SQLException {
        // 获得 fetchSize 。非空,则进行设置
        Integer fetchSize = mappedStatement.getFetchSize();
        if (fetchSize != null) {
            stmt.setFetchSize(fetchSize);
            return;
        }
        // 获得 defaultFetchSize 。非空,则进行设置
        Integer defaultFetchSize = configuration.getDefaultFetchSize();
        if (defaultFetchSize != null) {
            stmt.setFetchSize(defaultFetchSize);
        }
    }
    

5. SimpleStatementHandler

org.apache.ibatis.executor.statement.SimpleStatementHandler ,继承 BaseStatementHandler 抽象类,java.sql.Statement 的 StatementHandler 实现类。

5.1 构造方法

// SimpleStatementHandler.java

public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}

5.2 instantiateStatement

#instantiateStatement() 方法,创建 java.sql.Statement 对象。代码如下:

// SimpleStatementHandler.java

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

5.3 parameterize

// SimpleStatementHandler.java

@Override
public void parameterize(Statement statement) throws SQLException {
    // N/A
}
  • 空,因为无需做占位符参数的处理。

5.4 query

// SimpleStatementHandler.java

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    // <1> 执行查询
    statement.execute(sql);
    // <2> 处理返回结果
    return resultSetHandler.handleResultSets(statement);
}
  • <1> 处,调用 Statement#execute(String sql) 方法,执行查询。
  • <2> 处,调用 ResultHandler#handleResultSets(Statement stmt) 方法,处理返回结果。

5.5 queryCursor

// SimpleStatementHandler.java

@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    // <1> 执行查询
    statement.execute(sql);
    // <2> 处理返回的 Cursor 结果
    return resultSetHandler.handleCursorResultSets(statement);
}
  • <1> 处,调用 Statement#execute(String sql) 方法,执行查询。
  • <2> 处,调用 ResultHandler#handleCursorResultSets(Statement stmt) 方法,处理返回的 Cursor 结果。

5.6 batch

// SimpleStatementHandler.java

@Override
public void batch(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    // 添加到批处理
    statement.addBatch(sql);
}
  • 调用 Statement#addBatch(String sql) 方法,添加到批处理。

5.7 update

// SimpleStatementHandler.java

@Override
public int update(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    int rows;
    // 如果是 Jdbc3KeyGenerator 类型
    if (keyGenerator instanceof Jdbc3KeyGenerator) {
        // <1.1> 执行写操作
        statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
        // <2.2> 获得更新数量
        rows = statement.getUpdateCount();
        // <1.3> 执行 keyGenerator 的后置处理逻辑
        keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
    // 如果是 SelectKeyGenerator 类型
    } else if (keyGenerator instanceof SelectKeyGenerator) {
        // <2.1> 执行写操作
        statement.execute(sql);
        // <2.2> 获得更新数量
        rows = statement.getUpdateCount();
        // <2.3> 执行 keyGenerator 的后置处理逻辑
        keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
    } else {
        // <3.1> 执行写操作
        statement.execute(sql);
        // <3.2> 获得更新数量
        rows = statement.getUpdateCount();
    }
    return rows;
}
  • 根据 keyGenerator 的类型,执行的逻辑,略有差异。
  • <1.1><1.2><1.3> 处,调用 Statement#execute(String sql, ...) 方法,执行写操作。其中,<1.1> 比较特殊,使用数据自带的自增功能。
  • <1.2><2.2><3.2> 处,调用 Statement#getUpdateCount() 方法,获得更新数量。
  • <1.3><2.3> 处,调用 KeyGenerator#processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) 方法,执行 keyGenerator 的后置处理逻辑。
  • 虽然有点长,逻辑还是很清晰的。

6. PreparedStatementHandler

org.apache.ibatis.executor.statement.PreparedStatementHandler ,继承 BaseStatementHandler 抽象类,java.sql.PreparedStatement 的 StatementHandler 实现类。

6.1 构造方法

// PreparedStatementHandler.java

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

6.2 instantiateStatement

#instantiateStatement() 方法,创建 java.sql.Statement 对象。代码如下:

// PreparedStatementHandler.java

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    // <1> 处理 Jdbc3KeyGenerator 的情况
    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);
        }
    // <2>
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
        return connection.prepareStatement(sql);
    // <3>
    } else {
        return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
}
  • <1> 处,处理 Jdbc3KeyGenerator 的情况。
  • <2> + <3> 处,和 SimpleStatementHandler 的方式是一致的。

6.3 parameterize

// PreparedStatementHandler.java

@Override
public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
}
  • 调用 ParameterHandler#setParameters(PreparedStatement ps) 方法,设置 PreparedStatement 的占位符参数。

6.4 query

// PreparedStatementHandler.java

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行查询
    ps.execute();
    // 处理返回结果
    return resultSetHandler.handleResultSets(ps);
}

6.5 queryCursor

// PreparedStatementHandler.java

@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行查询
    ps.execute();
    // 处理返回的 Cursor 结果
    return resultSetHandler.handleCursorResultSets(ps);
}

6.6 batch

// PreparedStatementHandler.java

@Override
public void batch(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 添加到批处理
    ps.addBatch();
}

6.7 update

// PreparedStatementHandler.java

@Override
public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行写操作
    ps.execute();
    int rows = ps.getUpdateCount();
    // 获得更新数量
    Object parameterObject = boundSql.getParameterObject();
    // 执行 keyGenerator 的后置处理逻辑
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
}

7. CallableStatementHandler

org.apache.ibatis.executor.statement.CallableStatementHandler ,继承 BaseStatementHandler 抽象类,java.sql.CallableStatement 的 StatementHandler 实现类。

因为本系列不分享存储过程相关的内容,所以省略。感兴趣的胖友,自己研究哈。

8. 创建 StatementHandler 对象

在上面的文章中,我们已经看了各种 StatementHandler 的实现代码。那么StatementHandler 对象究竟在 MyBatis 中是如何被创建的呢Configuration 类中,提供 #newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 方法,代码如下:

// Configuration.java

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // <1> 创建 RoutingStatementHandler 对象
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 应用插件
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}
  • <1> 处,创建 RoutingStatementHandler 对象。通过它,自动路由到适合的 StatementHandler 实现类。😈 此处一看,更加适合,使用工厂模式。
  • <2> 处,应用插件。关于插件,我们在后续的文章中,详细解析。

666. 彩蛋

比较简单,都是咱们熟悉的 Statement 的方法封装。

参考和推荐如下文章: