1. 概述
本文,我们来分享 SQL 执行的第二部分,statement
包。整体类图如下:
类图
- 我们可以看到,整体是以 StatementHandler 为核心。所以,本文主要会看到的就是 StatementHandler 对 JDBC Statement 的各种操作。
而 StatementHandler 在整个 SQL 执行过程中,所处的位置如下:
整体流程
2. StatementHandler
org.apache.ibatis.executor.statement.StatementHandler
,Statement 处理器,其中 Statement 包含 java.sql.Statement
、java.sql.PreparedStatement
、java.sql.CallableStatement
三种。代码如下:
public interface StatementHandler {
Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
void parameterize(Statement statement) throws SQLException;
void batch(Statement statement) throws SQLException;
int update(Statement statement) throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(Statement statement) throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
|
StatementHandler 有多个子类,如下图所示:
类图
- 左边的三个实现类,分别对应
java.sql.Statement
、java.sql.PreparedStatement
、java.sql.CallableStatement
三种不同的实现类。
- 右边的 RoutingStatementHandler 实现类,负责将不同的 Statement 类型,路由到上述三个实现类上。
下面,我们先看右边的实现类,再看左边的实现类。
3. RoutingStatementHandler
org.apache.ibatis.executor.statement.RoutingStatementHandler
,实现 StatementHandler 接口,路由的 StatementHandler 对象,根据 Statement 类型,转发到对应的 StatementHandler 实现类中。
3.1 构造方法
private final StatementHandler delegate;
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()); } }
|
- 根据不同的类型,创建对应的 StatementHandler 实现类。
- 😈 经典的装饰器模式。实际上,有点多余。。。还不如改成工厂模式。
3.2 实现方法
所有的实现方法,调用 delegate
对应的方法即可。代码如下:
@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 构造方法
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) { this.configuration = mappedStatement.getConfiguration();
this.executor = executor; this.mappedStatement = mappedStatement; this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { generateKeys(parameterObject); boundSql = mappedStatement.getBoundSql(parameterObject); } this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); }
|
4.2 prepare
@Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { statement = instantiateStatement(connection); setStatementTimeout(statement, transactionTimeout); 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 对象。这是一个抽象方法,需要子类去实现。代码如下:
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
|
<2>
处,调用 #setStatementTimeout(Statement stmt, Integer transactionTimeout)
方法,设置超时时间。代码如下:
protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException { 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
。代码如下:
protected void setFetchSize(Statement stmt) throws SQLException { Integer fetchSize = mappedStatement.getFetchSize(); if (fetchSize != null) { stmt.setFetchSize(fetchSize); return; } 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 构造方法
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
对象。代码如下:
@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
@Override public void parameterize(Statement statement) throws SQLException { }
|
5.4 query
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { String sql = boundSql.getSql(); statement.execute(sql); return resultSetHandler.handleResultSets(statement); }
|
<1>
处,调用 Statement#execute(String sql)
方法,执行查询。
<2>
处,调用 ResultHandler#handleResultSets(Statement stmt)
方法,处理返回结果。
5.5 queryCursor
@Override public <E> Cursor<E> queryCursor(Statement statement) throws SQLException { String sql = boundSql.getSql(); statement.execute(sql); return resultSetHandler.handleCursorResultSets(statement); }
|
<1>
处,调用 Statement#execute(String sql)
方法,执行查询。
<2>
处,调用 ResultHandler#handleCursorResultSets(Statement stmt)
方法,处理返回的 Cursor 结果。
5.6 batch
@Override public void batch(Statement statement) throws SQLException { String sql = boundSql.getSql(); statement.addBatch(sql); }
|
- 调用
Statement#addBatch(String sql)
方法,添加到批处理。
5.7 update
@Override public int update(Statement statement) throws SQLException { String sql = boundSql.getSql(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); int rows; if (keyGenerator instanceof Jdbc3KeyGenerator) { statement.execute(sql, Statement.RETURN_GENERATED_KEYS); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else if (keyGenerator instanceof SelectKeyGenerator) { statement.execute(sql); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else { statement.execute(sql); 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 构造方法
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
对象。代码如下:
@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); } }
|
<1>
处,处理 Jdbc3KeyGenerator 的情况。
<2>
+ <3>
处,和 SimpleStatementHandler 的方式是一致的。
6.3 parameterize
@Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); }
|
- 调用
ParameterHandler#setParameters(PreparedStatement ps)
方法,设置 PreparedStatement 的占位符参数。
6.4 query
@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
@Override public <E> Cursor<E> queryCursor(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.handleCursorResultSets(ps); }
|
6.6 batch
@Override public void batch(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.addBatch(); }
|
6.7 update
@Override public int update(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); int rows = ps.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); 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)
方法,代码如下:
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) interceptorChain.pluginAll(statementHandler); return statementHandler; }
|
<1>
处,创建 RoutingStatementHandler 对象。通过它,自动路由到适合的 StatementHandler 实现类。😈 此处一看,更加适合,使用工厂模式。
<2>
处,应用插件。关于插件,我们在后续的文章中,详细解析。
666. 彩蛋
比较简单,都是咱们熟悉的 Statement 的方法封装。
参考和推荐如下文章: