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

不念不忘少年蓝@ 2022-05-23 10:36 395阅读 0赞

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

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

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

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

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

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

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

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

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

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

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

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

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

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

20180607140418343

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

  1. public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  2. executorType = executorType == null ? defaultExecutorType : executorType;
  3. executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  4. Executor executor;
  5. if (ExecutorType.BATCH == executorType) {
  6. executor = new BatchExecutor(this, transaction);
  7. } else if (ExecutorType.REUSE == executorType) {
  8. executor = new ReuseExecutor(this, transaction);
  9. } else {
  10. executor = new SimpleExecutor(this, transaction);
  11. }
  12. if (cacheEnabled) {//全局设置,是否开启二级缓存,这样看来如果全局关闭,mapper中怎么设置都无法生效啊!
  13. executor = new CachingExecutor(executor);
  14. }
  15. executor = (Executor) interceptorChain.pluginAll(executor);
  16. return executor;
  17. }

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

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

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

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

二级缓存的开启:

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

  1. <settings>
  2. <setting name="cacheEnabled" value="true"/>
  3. </settings>

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

  1. <cache/>

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

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

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

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

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

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

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

发表评论

表情:
评论列表 (有 0 条评论,395人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Mybatis二级缓存剖析

    ⼆级缓存构建在⼀级缓存之上,在收到查询请求时,MyBatis 首先查询⼆级缓存,若⼆级缓存未命 中,再去查询⼀级缓存,⼀级缓存没有,再查询数据库。 与⼀级缓存不同,⼆级缓存和