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

670 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 精尽 MyBatis 源码分析 —— SQL 执行(二)之 StatementHandler
# 1. 概述
本文,我们来分享 SQL 执行的第二部分,`statement` 包。整体类图如下:[![类图](22-mybatis-SQL 执行(二)之 StatementHandler.assets/01.png)](http://static.iocoder.cn/images/MyBatis/2020_03_03/01.png)类图
- 我们可以看到,整体是以 StatementHandler 为核心。所以,本文主要会看到的就是 StatementHandler 对 JDBC Statement 的各种操作。
而 StatementHandler 在整个 SQL 执行过程中,所处的位置如下:[![整体流程](22-mybatis-SQL 执行(二)之 StatementHandler.assets/02.png)](http://static.iocoder.cn/images/MyBatis/2020_03_03/02.png)整体流程
# 2. StatementHandler
`org.apache.ibatis.executor.statement.StatementHandler` Statement 处理器,其中 Statement 包含 `java.sql.Statement``java.sql.PreparedStatement``java.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 有多个子类,如下图所示:[![类图](http://static.iocoder.cn/images/MyBatis/2020_03_03/03.png)](http://static.iocoder.cn/images/MyBatis/2020_03_03/03.png)类图
- 左边的三个实现类,分别对应 `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 构造方法
```
// 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` 为空:
- 一般是**写**类操作,例如:`insert``update``delete` 。代码如下:
```
// 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 对象。其中,方法参数 `boundSql` 为 `null` 。
- `<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);
}
```
- 从代码中,可以看到,创建的是 DefaultParameterHandler 对象。而这个类,在 [《精尽 MyBatis 源码分析 —— SQL 初始化(下)之 SqlSource》](http://svip.iocoder.cn/MyBatis/scripting-2) 的 [「7.1 DefaultParameterHandler」](https://svip.iocoder.cn/MyBatis/executor-2/#) 已经有详细解析啦。
- `<3>` 处,创建 ResultSetHandler 对象。详细解析,见 [《精尽 MyBatis 源码分析 —— SQL 执行(四)之 ResultSetHandler》](http://svip.iocoder.cn/MyBatis/executor-4) 。
## 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);
}
}
```
- 感兴趣的胖友,可以看看 [《聊聊jdbc statement的fetchSize》](https://juejin.im/post/5a6757e351882573541c86bb) 和 [《正确使用MySQL JDBC setFetchSize()方法解决JDBC处理大结果集 java.lang.OutOfMemoryError: Java heap space》](https://blog.csdn.net/seven_3306/article/details/9303879) 。
# 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 的方法封装。
参考和推荐如下文章:
- 祖大俊 [《Mybatis3.3.x技术内幕StatementHandlerBox stop here](https://my.oschina.net/zudajun/blog/668378)
- 徐郡明 [《MyBatis 技术内幕》](https://item.jd.com/12125531.html) 的 [「3.5 StatementHandler](https://svip.iocoder.cn/MyBatis/executor-2/#) 小节