MyBatis缓存的底层原理(源码分析)

MyBatis缓存的底层原理(源码分析)

在分析之前,先明确两个要点
  • 缓存的执行顺序: 二级缓存 —> 一级缓存 —> 数据库
    ①到二级缓存中去找,因为二级缓存中可能有其它SqlSession的缓存
    ②如果二级缓存没找到,就到一级缓存中找
    ③一级缓存中没找到就到数据库中查
  • 缓存的实质:缓存的实质就是一个Map,key对应一个具体的缓存标识,value为缓存的内容。

一、Cache接口及其实现类

在这里插入图片描述
org.apache.ibatis.cache.impl.PerpetualCache是Mybatis的默认实现,PerpetualCache会同时操作Mybatis的一级缓存和二级缓存的数据,它通过两个不同的调用者分别实现一级缓存和二级缓存使用同一个实现处理

  • 一级缓存: BaseExecutor调用PerpetualCache
  • 二级缓存: CachingExecutor调用PerpetualCache

二、一级缓存

1、进入二级缓存中查找

CachingExecutor类
@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) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        
        //因为缓存的实质就是一个Map,所以拿到key,到cache中找目标缓存
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          //如果没找到就到一级缓存中去找,调用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、如果二级缓存没有命中,则进入一级缓存中寻找

BaseExecutor类
@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.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      /*
      判断结果集是否为null,如果为null,则到loaclCache(一级缓存数据存放位置)通过key去找去找
      	如果缓存中有,则赋给list
      */
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {//判断是否命中缓存,如果!=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();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

三、二级缓存

说明: 二级缓存执行流程与一级缓存一致,但使用Mybatis与EHCache二级缓存的内容不一样,EHCache内容会更丰富,健壮性稍强。

附加:对比Mybatis自带的二级缓存与EHCache缓存对象的结构对比

【1】Mybatis二级缓存

在这里插入图片描述

【2】EHCache二级缓存

在这里插入图片描述

总结

    缓存的实质就是一个Map,key对应一个具体的缓存标识,value为缓存的内容。执行查询时会先经过
CachingExecutor类中的query方法,查看二级缓存中是否有对应的缓存,如果没有再进入一级缓存中查找,
如果一级缓存中也没有,最后到数据库中查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值