Mybatis执行器源码分析Executor
概述
我们平时使用的时候很少关注这个Executor,往往一般是直接使用sqlSession进行数据操作,但是我们知道sqlSession拥有configruation 和Excutor,configruation我们不用说,所有的配置信息都是这个类来完成,但是操作数据库还是有Excutor来完成,但是我们应该知道sqlSession就是进行封装一层方便用户调用,但是底层如何进行数据操作的哪?以及平时大家说的一级缓存,二级缓存,事务这些概念应该都是在Executor级别上搞定事情?我们今天进行一探究竟
结构图
sqlSession 是门面,方便用户操作
Executor提供了数据操作和一些辅助操作api接口
CachingExecutor:提供二级缓存功能
BaseExecutor: 提供一级缓存和获取链接等一些公共的方法实现,是一个抽象类;
- 简单执行器
simpleExecutor,每次执行SQL需要预编译SQL语句。
- 可重用执行器
ReuseExecutor,同一SQL语句执行只需要预编译一次SQL语句
- 批处理执行器
BatchExecutor,只针对修改操作的SQL语句预编译一次,并且需要手动刷新SQL执行才生效。
- 执行器抽象类
BaseExecutor,执行上面3个执行器的重复操作,比如一级缓存、doQuery、doUpdate方法。
- 二级缓存
CachingExecutor,与一级缓存的区别:一级缓存查询数据库操作后会直接缓存,二级缓存需要当次数据库操作提交事务后才能进行缓存(二级缓存跨线程处理,一级缓存不用)。
测试一把
测试代码
public class ExecutorTest {
private Configuration configuration;
private Connection connection;
private JdbcTransaction jdbcTransaction;
private SqlSessionFactory build;
private void init() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
build = new SqlSessionFactoryBuilder().build(inputStream);
configuration = build.getConfiguration();
jdbcTransaction = new JdbcTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.SERIALIZABLE,true);
}
@Test
public void simpleExecutorTest() throws Exception {
init();
SimpleExecutor executor = new SimpleExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.wfg.ActivityMapper.getActivity");
executor.doQuery(ms,1, RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER,ms.getBoundSql(10));
executor.doQuery(ms,1, RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER,ms.getBoundSql(10));
executor.doQuery(ms,1, RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER,ms.getBoundSql(10));
}
@Test
public void reuseExecutorTest() throws Exception {
init();
ReuseExecutor executor = new ReuseExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.wfg.ActivityMapper.getActivity");
executor.doQuery(ms,1, RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER,ms.getBoundSql(10));
executor.doQuery(ms,1, RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER,ms.getBoundSql(10));
executor.doQuery(ms,1, RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER,ms.getBoundSql(10));
}
//批处理执行器
//只针对修改操作
//批处理操作必须手动刷新
@Test
public void batchExecutorTest() throws Exception {
init();
BatchExecutor executor = new BatchExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.wfg.ActivityMapper.getActivity");
executor.doQuery(ms,1, RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER,ms.getBoundSql(10));
executor.doQuery(ms,1, RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER,ms.getBoundSql(10));
MappedStatement ms2 = configuration.getMappedStatement("com.wfg.ActivityMapper.setName");
HashMap<String,Object> map = new HashMap<>();
map.put("id",1);
map.put("name","test");
executor.doUpdate(ms2,map);
HashMap<String,Object> map2 = new HashMap<>();
map2.put("id",2);
map2.put("name","test2333");
executor.doUpdate(ms2,map2);
HashMap<String,Object> map3 = new HashMap<>();
map3.put("id",3);
map3.put("name","test3");
executor.doUpdate(ms2,map3);
// executor.flushStatements();
}
@Test
public void baseExecutorTest() throws Exception{
init();
MappedStatement ms = configuration.getMappedStatement("com.wfg.ActivityMapper.getActivity");
Executor executor = new ReuseExecutor(configuration,jdbcTransaction);
executor.query(ms,2,RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
executor.query(ms,2,RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
}
@Test
public void cacheExecutorTest() throws Exception{
init();
MappedStatement ms = configuration.getMappedStatement("com.wfg.ActivityMapper.getActivity");
//二级缓存相关逻辑 执行数据库操作
Executor executor = new CachingExecutor(new ReuseExecutor(configuration,jdbcTransaction));
executor.query(ms,2,RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
executor.commit(true); //1.二级缓存需要先进行提交,二级缓存是跨线程的 1,先走二级缓存2,再走一级缓存
executor.query(ms,2,RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
executor.query(ms,2,RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
}
@Test
public void SqlSessTest() throws Exception{
init();
SqlSession sqlSession = build.openSession();
ActivityMapper mapper = sqlSession.getMapper(ActivityMapper.class);
mapper.getActivity(1);
sqlSession.commit(true);
mapper.getActivity(1);
mapper.getActivity(1);
}
}
simpleExecutor
reuseExecutor
预编译一次
batchExecutor
从效果上可以看出只有update好使
baseExecutor
从效果上可以看到一级缓存起作用
cachingExecutor
二级缓存起作用
使用sqlSession
public void SqlSessTest() throws Exception{
init();
SqlSession sqlSession = build.openSession();
ActivityMapper mapper = sqlSession.getMapper(ActivityMapper.class);
mapper.getActivity(2);
sqlSession.commit(true);
mapper.getActivity(1);
sqlSession.commit(true);
mapper.getActivity(2);
sqlSession.commit(true);
mapper.setName(2,"zhangsan");
sqlSession.commit(true);
mapper.getActivity(2);
sqlSession.commit(true);
}
庖丁解牛
看到上面这些测试用例我有一下几个问题?
1,这几个Executor是什么关系,怎么相互依赖的,其中使用代码是如何落实的
2.sqlSession默认执行逻辑是如何运行?
3.sqlSession如何初始化的Executor?
4.simple,reuse, bathc 这三个Exector我们如何选择?
好了带着这几个问题我们进行源码查看:
获取sqlSessiong
SqlSession sqlSession = build.openSession();
@Override
public SqlSession openSession() {
// 这里我们知道可以设置Excutor 问题4:解决
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//获取:Executor 根据传入的execType
final Executor executor = configuration.newExecutor(tx, execType);
//将configuration 和 excutor 注入sqlSessiong 并返回
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();
}
}
获取Excutor 逻辑:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//根据传入的类型创建BaseExecutor 其实是三个实现类的其中之一
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);
}
//如果开启二级缓存 创建 CachingExecutor 并将上面创建的baseExecutor 注入到 CachingExecutor 此处使用的装饰模式
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
执行数据库逻辑
mapper.getActivity(2);
org.apache.ibatis.executor.CachingExecutor#query
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);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) { //配置缓存类
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {//用二级缓存并且resultHandler 为空
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
//从缓存中获取缓存数据,二级缓存的key和一级缓存的key规则一样
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) { //未获取到二级缓存数据
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//如果没有配置二级缓存直接执行baseExecutor query方法
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
未获取到二级缓存数据
源码分析参考 一级源码分析
总结
执行逻辑:
- 首先执行cachingExecutor中的query方法,获取二级缓存中的数据,如果可以获取到直接返回,流程结束,如果没获取到数据,进行进入BaseExecutor中的query方法,并将返回的数据保存到二级缓存中
- BaseExecutor先执行query方法,从一级缓存中获取,如果获取成功返回,如果没有获取成功则执行simpli,reuse,bathc中的doquery方法,并将返回的数据存储到一级缓存中
- 三个实现类调用数据查询