Mybatis 源码学习(四) 二级缓存

本文详细解析了MyBatis中的二级缓存机制,包括其配置方式、工作原理及如何在代码中实现。通过实际案例展示了二级缓存的使用方法,并强调了全局配置的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

听说二级缓存是Mapper级的,我找变了mapper也没有找到。

不小心看到MappedStatement有个cache属性。难道是在这里。开始没能串起来。只能网上找。

最后确认这个二级缓存果然就是在MappedStatement。而它又是在Configuration上的啊,这不就成了全局的了吗。

猜:只是缓存的时候以mapper为一个组,然后清理的时候按mapper去清理缓存。所以就成了是Mapper级的了。也就是说是以Mapper去管理这个缓存的。

找的过程就不详细记录了。

要知道二级缓存是可以设置开关的,而且除了全局的开关,还可以在具体的mapper里单独设置。怎么实现的呢?

还有具体查的时候,怎么查的MappedStatement中的cache呢?

还是从最简单的DefaultSqlSession的selectOne(String statement) 方法开始看起。

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);//就是这里,在学一级缓存的时候就看过这个方法了
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

这executor.query 有两个实现类。一个是CachingExecutor,另一个是BaseExecutor。我们看CachingExecutor的query

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();//从MappedStatement中获取cache。这里我们就知道从这里获取的二级缓存,之前的推测正确,二级缓存就是MappedStatement的cache。
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {//这里的isUseCache是在Mapper文件中设置的缓存开关
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);//缓存中没有再去BaseExcutor中查(二级缓存没有再找一级缓存)
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

这里我们就弄明白了,如何从二级缓存中去数据了。

那么这个executor到底是CachingExecutor还是BaseExecutor呢?

那就要看SqlSession中的executor是如何被创建的


来看这个newExecutor方法,这是我看到的最有价值的一个方法。

  public Executor newExecutor(Transaction transaction, ExecutorType 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);
    }
    if (cacheEnabled) {//全局设置,是否开启二级缓存,这样看来如果全局关闭,mapper中怎么设置都无法生效啊!
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }    

开始以为ExecutorType里会有CachingExecutor呢。后来发现不是这里的这个type并不是。

这里有BaseExecutor、SimleExecutor、ReuseExecutor、BatchExecutor、CachingExecutor,是什么关系呢。

SimleExecutor、ReuseExecutor、BatchExecutor是BaseExecutor的子类,CachingExecutor感觉更像是个代理类。

关于二级缓存呢,就看有没有套CachingExecutor这个代理类,跟三个子类没有关系。

二级缓存的开启:

1.在配置文件中天津配置

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

2.Mapper中添加配置 最简单的配置还可以配置 最大缓存数量 刷新时间 移除策略

<cache/>

3.如果有某个statement不想用二级缓存的话 就可以添加一个配置 这个配置默认为true,只用不使用时才配置

    <select id="selectUser" resultMap="UserMapper" parameterType="Long" useCache="false">

补充一下我的测试代码,开始一直看不到二级缓存到底是怎么生效的,测试代码更让人迷惑,debug看就是找不到。

    public static void main( String[] args )
    {
        String resource = "mybatis-config.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            User user = sqlSession.selectOne("selectUser",1L);
            sqlSession.commit(); //这里从网上看到的,的确是不加commit不行,还以为存到别的地方了呢。
            SqlSession sqlSession1 = sqlSessionFactory.openSession();
            sqlSession1.selectOne("selectUser",1L);
            user.setName("autotester1");
            sqlSession.update("updateUserName",user);
            user = sqlSession.selectOne("selectUser",1L);
            System.out.println(user.getName());
            //UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //System.out.println(userMapper);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

关键在那个commit,之前没有commit,怎么也不生效。

二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace)并且可自定义存储源,如 Ehcache。

从这里学到的:https://www.cnblogs.com/xdp-gacl/p/4270403.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值