Mybatis执行器源码分析Executor

概述

我们平时使用的时候很少关注这个Executor,往往一般是直接使用sqlSession进行数据操作,但是我们知道sqlSession拥有configruation 和Excutor,configruation我们不用说,所有的配置信息都是这个类来完成,但是操作数据库还是有Excutor来完成,但是我们应该知道sqlSession就是进行封装一层方便用户调用,但是底层如何进行数据操作的哪?以及平时大家说的一级缓存,二级缓存,事务这些概念应该都是在Executor级别上搞定事情?我们今天进行一探究竟

结构图

在这里插入图片描述
sqlSession 是门面,方便用户操作
Executor提供了数据操作和一些辅助操作api接口
CachingExecutor:提供二级缓存功能
BaseExecutor: 提供一级缓存和获取链接等一些公共的方法实现,是一个抽象类;

  1. 简单执行器

simpleExecutor,每次执行SQL需要预编译SQL语句。

  1. 可重用执行器

ReuseExecutor,同一SQL语句执行只需要预编译一次SQL语句

  1. 批处理执行器

BatchExecutor,只针对修改操作的SQL语句预编译一次,并且需要手动刷新SQL执行才生效。

  1. 执行器抽象类

BaseExecutor,执行上面3个执行器的重复操作,比如一级缓存、doQuery、doUpdate方法。

  1. 二级缓存

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);
  }

未获取到二级缓存数据

源码分析参考 一级源码分析

总结

执行逻辑:

  1. 首先执行cachingExecutor中的query方法,获取二级缓存中的数据,如果可以获取到直接返回,流程结束,如果没获取到数据,进行进入BaseExecutor中的query方法,并将返回的数据保存到二级缓存中
  2. BaseExecutor先执行query方法,从一级缓存中获取,如果获取成功返回,如果没有获取成功则执行simpli,reuse,bathc中的doquery方法,并将返回的数据存储到一级缓存中
  3. 三个实现类调用数据查询
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值