紧接上一篇,现在就来看看 StatementHandler
是如何从 Executor
手中接棒并完成后续操作的吧。
StatementHandler 分类
可以看到 StatementHandler
的类关系和 Executor
非常相似。
其中 BaseStatementHandler
的三个子类都是和 JDBC
中的 Statement
相对应的。
SimpleStatementHandler
对应JDBC
中常用的Statement
接口PreparedStatementHandler
对应JDBC
中PrepareStatement
,用于预编译的 SQL 接口CallableStatementHandler
对应JDBC
中的CallableStatement
,用于执行存储过程相关的接口RoutingStatementHandler
是上面三个接口的路由,没有实际操作,只是负责它们的创建和调用
创建 StatementHandler
创建 StatementHandler
是在 Configuration
中完成的
//org.apache.ibatis.session.Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建 StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//熟悉的身影,插件的支持
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
具体看一下 RoutingStatementHandler
的创建逻辑
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());
}
}
就是这么简单,根据 MappedStatement
的 statementType
来决定创建那种 StatementHandler
,默认是 PREPARED
。也可以通过 mapper.xml
文件对应的 statement
节点上指定 statementType
,例如指定创建 SimpleStatementHandler
:
<select id="findLastPage" resultMap="userResult" statementType="STATEMENT">
SELECT * FROM user ORDER BY `update_time` DESC LIMIT 10
</select>
创建 Statement
通过上面创建的 StatementHandler
来创建 JDBC
的 Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
//①预备 Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//②对得到的 Statement 做参数化处理
handler.parameterize(stmt);
return stmt;
}
预备 Statement
预备 Statement
调用的是 BaseStatementHandler#prepare
方法
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//实例化 statement
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);
}
}
其中实例化操作 instantiateStatement
是一个抽象方法,需要子类去实现,这样就实现了不同的 StatementHandler
产生不一样的 Statement
的效果。实际上就是一开始说的 BaseStatementHandler
三个子类与 JDBC
的 Statement
的对应关系。
Statement 参数化处理
Statement
参数化处理是通过 BaseStatementHandler#parameterize
方法来完成的,一起来看看三个子类是如何处理的。
//SimpleStatementHandler 对应的是一般的 Statement,不需要参数化处理,所以是空实现
public void parameterize(Statement statement) {
// N/A
}
//PreparedStatementHandler 直接通过 ParameterHandler 完成参数化处理
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
//CallableStatementHandler 的参数化处理和 PreparedStatementHandler 一样依赖于 ParameterHandler
public void parameterize(Statement statement) throws SQLException {
registerOutputParameters((CallableStatement) statement);
parameterHandler.setParameters((CallableStatement) statement);
}
这下我们清楚了,需要进行参数化处理的 Statement
都交给 ParameterHandler#setParameters
去完成了。ParameterHandler
只有一个默认实现 DefaultParameterHandler
,具体的参数解析逻辑代码我就不贴了,感兴趣的自己去看看 ->_->
SQL 执行
获得 Statement
实例后,回到 SimpleExecutor#doQuery
方法中看看接下来是如何完成 SQL 的执行的吧。这里为了不用翻 Executor
的文章,就再贴一下代码。
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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
//看这里,最后一步
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
直接调用 StatementHandler#query
方法完成了查询操作,点进去看看有啥骚操作
//org.apache.ibatis.executor.statement.SimpleStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
// JDBC
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}
//org.apache.ibatis.executor.statement.PreparedStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// JDBC
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
//org.apache.ibatis.executor.statement.CallableStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
// JDBC
cs.execute();
List<E> resultList = resultSetHandler.handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}
是不是😥了?就直接 JDBC 了,并且最后都会调用 ResultSetHandler#handleResultSets
方法完成结果集的解析,默认实现也就只有 DefaultResultSetHandler
,感兴趣的自己看看吧。
总结
到这里处理缓存处理、插件支持没有介绍外,大体的流程也都串通了。
- 服务启动加载配置文件,创建
Configuration
,再通过SqlSessionFactoryBuilder
构建SqlSessionFactory
- 调用
SqlSessionFactory#openSession
创建SqlSession
实例,同时会创建一个Executor
SqlSession
的所有 CRUD 操作都会被委托给Executor
的query
和update
方法- 底层操作依赖于
JDBC
,所以就得创建Statement
,而StatementHandler
就是用来干这个的,在Executor
和JDBC
之间搭桥 - 在
JDBC
操作之前通过ParameterHandler
完成请求参数的解析,在操作之后通过ResultSetHandler
完成结果集的解析。
虽然举的例子是查询方法,但更新方法的整个流程也是一样的,感兴趣的自己断点调试调试吧😁