Pre
MyBatis源码-深入理解MyBatis Executor的设计思想
工程部分见 MyBatis源码- SqlSession门面模式 & selectList 源码解析
实际中,我们都是面向SqlSession编程的,不会直接调用Executor来执行业务逻辑,这里我们仅仅是为了深入了解下Executor体系架构才这么搞的,切记。
Executor 执行器
接口继承关系
这里我们重点看下Executor的 三个实现子类。
分别是:SimpleExecutor(简单执行器)、ReuseExecutor(重用执行器)、BatchExecutor(批处理执行器)。
ReuseExecutor(重用执行器)
回归下JDBC中的 Statement , 再和MyBatis 所封装的 对比一下
PreparedStatement 支持预编译参数
MyBatis的ReuseExecutor就是利用了JDBC Statement的这个特点 来处理的。
入门小demo
代码语言:javascript复制 @Test
public void testReuseExecutor() throws SQLException {
// 通过factory.openSession().getConnection()实例化JdbcTransaction ,用于构建ReuseExecutor
jdbcTransaction = new JdbcTransaction(factory.openSession().getConnection());
// 映射SQL
ms = configuration.getMappedStatement("com.artisan.UserMapper.selectByid");
// 实例化ReuseExecutor
ReuseExecutor executor = new ReuseExecutor(configuration, jdbcTransaction);
// 调用doQuery执行查询
List<User> userList = executor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, ms.getBoundSql(1));
System.out.println(userList.get(0));
List<User> userList2 = executor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, ms.getBoundSql(1));
System.out.println(userList2.get(0));
}
执行结果
可以看到 相同的SQL语句 会缓存对应的PrepareStatement , 缓存的生命周期: 会话有效期
源码实现
Key 是 sql , Value 是 Statement
执行过程:
executor.doQuery ----> prepareStatement(handler, ms.getStatementLog())
---------> 见下方源码
代码语言:javascript复制 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
先判断本地缓存statementMap是否有数据,有的话从statementMap获取,没有的话建立Statement,并存入本地缓存statementMap 。
注意这个缓存的声明周期 是仅限于本次会话。 会话结束后,这些缓存都会被销毁掉。
区别于SimpleExecutor的实现,多了个本地缓存。 推荐使用ReuseExecutor 。