深入理解mybatis的实现细节

首先要知道,Mybatis中有一级缓存和二级缓存.
一级缓存是默认开启的,而且是不能关闭的,一级缓存是指SqlSession级别的缓存。 在同一个SqlSession中,相同的SQL语句,第一次查询会走数据库并将结果缓存,以后相同的查询直接从缓存中拿结果。
二级缓存是mapper级别的缓存,可以在不同sqlSession中共享,mapper以命名空间为单位创建缓存数据结构,需要手动开启,配置方式为:

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: ***
  configuration:
    cache-enabled: true  --开启

1、mybatis的一级缓存和二级缓存实现细节

1、一级缓存和二级缓存配置后怎么体现在代码里的?

配置后是怎么实现的,我来扒一扒源码:

思路:sqlsession需要由SqlSessionFactory.openSession()开启,打开openSession()方法调用的是openSessionFromDataSource()

/**
	DefaultSqlSessionFactory.openSessionFromDataSource()方法,创建sqlsession
*/
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执行器,比较常用的SimpleExecutor(使用一级缓存)和CachingExecutor(使用二级缓存),execType默认是SIMPLE
      //进入configuration.newExecutor(tx, execType)方法看的到创建细节
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建好的默认SqlSession,封装了配置信息configuration和执行器executor
      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();
    }
}


// 打开configuration.newExecutor(tx, execType)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // 获取executor的类型,如果没有指定,暂时默认SIMPLE
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      // BATCH 批量执行器
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //这里二级缓存开启后的体现,配置后cacheEnabled=true,就会创建CachingExecutor缓存执行器
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

2、mybatis的一级缓存和二级缓存怎么实现缓存的?

思路:接下来使用DefaultSqlSession.select()查询方法为例,说明一级缓存和二级缓存怎么实现缓存的
  @Override
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      /** configuration中维护了mappedStatements,这是mapper.xml文件解析后的信息封装保存在了configuration,
      mappedStatements是在IOC容器初始化时,调用SqlSessionFactoryBean类的afterPropertiesSet()方法触发解析所有的mapper文件的。(建议去看看整个过程怎么来的)
      可以通过String statement,一个类名.方法名的字符串查找到对应的Mapper方法封装后的MappedStatement
      */
      MappedStatement ms = configuration.getMappedStatement(statement);
      //根据创建sqlSession时,构建的是SimpleExecutor还是CachingExecutor分别进入不同的实现类,分别来看
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  1.如果是SimpleExecutor,会调用抽象父类BaseExecutor的实现
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {//解析sql,把#{},${}替换成?,方便后续StatementHandler替换参数
    BoundSql boundSql = ms.getBoundSql(parameter);
    //创建缓存key,因为mybatis默认开启一级缓存,需要使用sql+参数创建缓存的key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }
  
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    //queryStack 查询层数,  ms.isFlushCacheRequired()是否调用刷新缓存
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      // 先从缓存中取值
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        // 没有缓存,再向数据库发起查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache();
      }
    }
    return list;
  }
  
2. 回过头在看看CachingExecutor的query方法实现
  @Override
  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) {
      //是否需要刷新缓存,需要配置flushCacheRequired=true
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        //查询缓存,这里缓存是保存在CachingExecutor的TransactionalCacheManager里的,在TransactionalCacheManager里面
        //有个Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
        //二级缓存就是保存在这个map中的
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          //缓存中没有,再调用BaseExecutor的query查询
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //将结果缓存
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

2、后续待补充

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值