1. Executor执行器
1.1 Executor 体系
Executor 执行器接口,它有两个实现类,分别是 BaseExecutor 和 CachingExecutor。
- BaseExecutor:是一个抽象类,这种通过抽象的实现接口的方式是适配器设计模式之接口适配的体现,是 Executor 的默认实现,实现了大部分 Executor 接口定义的功能,降低了接口实现的难度。BaseExecutor 的子类有三个,分别是SimpleExecutor、ReuseExecutor 和 BatchExecutor。
- SimpleExecutor:简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个 Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象)。
- ReuseExecutor :可重用执行器,这里的重用指的是重复使用 Statement,它会在内部使用一个 Map 把创建的 Statement 都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement 对象,如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement 对象,并将其缓存起来。每个SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的 Statement 作用域是同一个 SqlSession。
- BatchExecutor:批处理执行器,用于将多个 SQL 一次性输出到数据库。
- CachingExecutor:缓存执行器,先从缓存中查询结果,如果存在,就返回;如果不存在,再委托给 Executor delegate 去数据库中取,delegate 可以是上面任何一个执行器,默认是 simple executor。
1.2 Executor 功能
Executor 功能:每一个 sql 语句的执行都会先到 Executor 执行器中在调用相应 StatementHandler 执行 jdbc 操作。
源码如下 SimpleExecutor 中的代码片段:
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();
//调用statmentHandler,通过控制器调用jdbc相关操作
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
如何指定使用哪一种Executor?
在 Mybatis 配置⽂件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给
DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。
<setting name="defaultExecutorType" value="REUSE" />
1.3 Executor创建过程分析
1、创建 SqlSession 时,可以指定 Executor 类型,由 mybatis 中 ExecutorType 枚举类来指定,默认不传参就是 SIMPLE 类型。
sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
2、在 DefaultSqlSessionFactory 中创建执行器。
public SqlSession openSession(ExecutorType execType) {
return this.openSessionFromDataSource(execType, (TransactionIsolationLevel)null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null; // 定义事务
DefaultSqlSession var8;
try {
// mybatis配置文件中所有信息都封装到Configuration中,获取环境id、datasource等信息
Environment environment = this.configuration.getEnvironment();
// 获取事物工厂
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
// 根据工厂创建事物对象,直接 return new JdbcTransaction(ds, level, autoCommit);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建执行器
Executor executor = this.configuration.newExecutor(tx, execType);
//默认executor是CachingExecutor
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
3、下面看执行器创建具体流程:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 默认创建简单执行器
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//cacheEnabled默认ture开启二级缓存
if (cacheEnabled) {
//把简单执行器传递给CachingExecutor中的delegate属性,
executor = new CachingExecutor(executor);
}
//mybatis责任链模式
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
4、执行流程图如下:
2. 批处理
mapper 文件
<insert id="insertTeacherBatch" useGeneratedKeys="true" keyProperty="id" parameterType="teacher">
insert into mybatis_test.teacher(name) values (#{name})
</insert>
测试代码
@Test
public void insertTeacherBatchTest() throws IOException {
try {
String resource = "mybatis-config.xml";
is = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
TeacherDao mapper = sqlSession.getMapper(TeacherDao.class);
for (int i = 1; i < 1000; i++) {
mapper.insertTeacherBatch(new Teacher("weishao" + i));
}
sqlSession.commit();
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
is.close();
}
}