MyBatis 四大组件之 Executor 执行器
每一个 SqlSession 都会拥有一个 Executor 对象,这个对象负责增删改查的具体操作,我们可以简单的将它理解为 JDBC中Statement 的封装版。
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 可以是上面任何一个执行器
Executor创建过程以及源码分析
上面我们分析完 SqlSessionFactory 的创建过程的准备工作后,我们下面就开始分析会话的创建以及 Executor 的执行过程。
在创建完 SqlSessionFactory 之后,调用其openSession
方法:
SqlSession sqlSession = factory.openSession();
SqlSessionFactory 的默认实现是 DefaultSqlSessionFactory,所以我们需要关心的就是 DefaultSqlSessionFactory 中的 openSession() 方法
openSession 调用的是openSessionFromDataSource
方法,传递执行器的类型,方法传播级别,是否自动提交,然后在 openSessionFromDataSource 方法中会创建一个执行器
public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 得到configuration 中的environment final Environment environment = configuration.getEnvironment(); // 得到configuration 中的事务工厂 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 获取执行器 final Executor executor = configuration.newExecutor(tx, execType); // 返回默认的SqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
调用 newExecutor 方法,根据传入的 ExecutorType 类型来判断是哪种执行器,然后执行相应的逻辑
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { // defaultExecutorType默认是简单执行器, 如果不传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); } // 如果允许缓存,则使用缓存执行器 // 默认是true,如果不允许缓存的话,需要手动设置 if (cacheEnabled) { executor = new CachingExecutor(executor); } // 插件开发。 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
ExecutorType 的选择:
ExecutorType 来决定 Configuration 对象创建何种类型的执行器,它的赋值可以通过两个地方进行赋值:
- 可以通过 settings 标签来设置当前工程中所有的 SqlSession 对象使用默认的 Executor
<settings> <!--取值范围 SIMPLE, REUSE, BATCH --> <setting name="defaultExecutorType" value="SIMPLE"/> </settings>
另外一种直接通过Java对方法赋值的方式
session = factory.openSession(ExecutorType.BATCH);
ExecutorType 是一个枚举,它只有三个值 SIMPLE, REUSE, BATCH
创建完成 Executor 之后,会把 Executor 执行器放入一个 DefaultSqlSession 对象中来对四个属性进行赋值,他们分别是 configuration
、executor
、 dirty
、autoCommit
。
Executor接口的主要方法
Executor 接口的方法还是比较多的,这里我们就几个主要的方法和调用流程来做一个简单的描述
大致流程
Executor 中的大部分方法的调用链其实是差不多的,下面都是深入源码分析执行过程,如果你没有时间或者暂时不想深入研究的话,给你下面的执行流程图作为参考。
query()方法
query 方法有两种形式,一种是直接查询;一种是从缓存中查询,下面来看一下源码
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
当有一个查询请求访问的时候,首先会经过 Executor 的实现类 CachingExecutor,先从缓存中查询 SQL 是否是第一次执行,如果是第一次执行的话,那么就直接执行 SQL 语句,并创建缓存,如果第二次访问相同的 SQL 语句的话,那么就会直接从缓存中提取
CachingExecutor.java
// 第一次查询,并创建缓存 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
MapperStatement
维护了一条 select、update、delete、insert 节点的封装,包括资源(resource),配置(configuration),SqlSource(sql源文件)等。使用Configuration 的 getMappedStatement 方法来获取 MappedStatement 对象