mybatis源码分析之06二级缓存

     2022-04-01     549

关键词:

上一篇整合redis框架作为mybatis的二级缓存, 该篇从源码角度去分析mybatis是如何做到的。

通过上一篇文章知道,整合redis时需要在FemaleMapper.xml中添加如下配置

<cache eviction="LRU" type="qinfeng.zheng.RedisCache"/>

 

MYBATIS源码分析之02配置文件解析  这篇文章讲解了mybatis解析配置文件具体的过程。

这里再总结一下,mybatis解析主配置文件使用了XMLConfigBuilder对象;解析具体的Mapper接口配置则使用了XMLMapperBuilder对象,是不是很好记忆。。。

 

所以,要了解mybatis是如何是解析  <cache eviction="LRU" type="qinfeng.zheng.RedisCache"/>  这行代码 ,肯定得去XMLMapperBuilder这个类了,

具体看XMLMapperBuilder#configurationElement(XNode  context)方法。

 

很明显,具体的实现在cacheElement()方法中, 结合debug看一下

 

接着我们可以再深究一下builderAssistant是如何创建这个缓存的。

  public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      boolean blocking,
      Properties props) {
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    configuration.addCache(cache);
    currentCache = cache;
    return cache;
  }

由上面的这段代码知道 ,mybatis框架使用了一个构建者模式去创建一个Cache对象。 对于复杂对象的创建,我认为使用构建者模式是很有必要的,代码起来也很会舒服!

我们看看build()方法中,具体是如何构造这个Cache对象的。

  public Cache build() {
    setDefaultImplementations();
    // 使用反射创建了一个Cache对象(真实类型就是我们的RedisCache)
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    // issue #352, do not apply decorators to custom caches
    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      cache = new LoggingCache(cache);
    }
    return cache;
  }

 

 

至此,应该已经说明了mybatis是如何解析二级缓存的配置的了!

 

***************************************************************************************************************

以下内容,我们看看mybatis是如何使用二级缓存的。

在说mybatis的一级缓存时,提到了二级缓存,就是CachingExecutor#query()方法,具体见下面这段代码

  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") 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; } } return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }

 

咱们还是debug分析

 

 

哈哈,这个tcm就跟二级缓存密切相关了,我们需要细读, 从getObject(cache,key)方法开始,我们会进入到TransactionalCache#getObject(Object key)方法。

 

F7再进入到LoggingCache#getObject(key)

 

哈哈,这里的delegate就是RedisCache了,getObject(key)就会调用我们自定义的逻辑了,也就是下面这段代码,从redis中去拿数据 

   @Override
    public Object getObject(Object key) {
        return execute(jedis -> {
            try {
                byte[] bytes = jedis.hget(id.getBytes(), key.toString().getBytes());
                if (bytes == null) {
                    return null;
                }
                ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
                ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
                return objectInputStream.readObject();
            } catch (Exception e) {
                throw new RuntimeException("get data from redis error!", e);
            }
        });
    }

 

至此,从二级缓存中getObject已经说完了,下面说下putObject, 我们还是从TransactionalCache#putObject(key,value)说起。 大家可以知道 ,mybatis的二级缓存与TransactionalCache脱离不了关系,mybatis的一级缓存与PerpetualCache 脱离不了关系。

好,言归正传,继续看TransactionalCache#putObject(key,value)方法

 @Override
  public void putObject(Object key, Object object) {
    entriesToAddOnCommit.put(key, object);
  }

 

大家可以看到,putObject方法只是将数据put到entriesToAddOnCommit这个Map集合中,并没有真定写入到redis中。

上篇已经说了,mybatis框架是在commit()时,将entriesToAddOnCommit中的数据写入到redis中, 我们可以简单看一下源码。

  public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    flushPendingEntries();
    reset();
  }
  private void flushPendingEntries() {
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }

 

 

 好,完了,下面总结一下!

 

总结;

(1) 如果MappedStatement中没有二级缓存,直接走一级缓存

(2) 如果MappedStatement中有二级缓存,则从二级缓存中查询数据

(3) 如果二级缓存中没有查询到数据,则走一级缓存(一级没有,就查库), 并且将从一级缓存中获取到的数据写入到TransactionalCache#entriesToAddOnCommit集合中,缓存起来

(4) 调用SqlSession#commit()方法时,会将TransactionalCache#entriesToAddOnCommit集合中缓存起来的数据写入到二级缓存中

 

 

 

 

 

 

mybatis源码分析五mybatis的缓存

五、MyBatis缓存文章目录五、MyBatis缓存缓存的概念与应用缓存的概念开发一个简单的缓存MyBatis中的缓存设计自定义一个Cache实现类MyBatis中的Cache实现类PerpetualCache装饰器CacheCache如何在MyBatis运行过程应用MyBatis缓存的二层体系一级... 查看详情

mybatis源码分析五mybatis的缓存

五、MyBatis缓存文章目录五、MyBatis缓存缓存的概念与应用缓存的概念开发一个简单的缓存MyBatis中的缓存设计自定义一个Cache实现类MyBatis中的Cache实现类PerpetualCache装饰器CacheCache如何在MyBatis运行过程应用MyBatis缓存的二层体系一级... 查看详情

mybatis缓存的使用和源码分析

Mybatis缓存使用在Mybatis中缓存分为一级缓存和二级缓存,二级缓存又称为全局缓存,默认一级缓存和二级缓存都是开启的,只是二级缓存的使用需要配置才能生效,在Mybatis中一级缓存是SqlSession级别也就是会话级别的,而二级缓... 查看详情

mybatis从入门到精通—源码剖析之二级缓存细节(代码片段)

⼆级缓存构建在⼀级缓存之上,在收到查询请求时,MyBatis⾸先会查询⼆级缓存,若⼆级缓存未命中,再去查询⼀级缓存,⼀级缓存没有,再查询数据库。⼆级缓存------》⼀级缓存------》数据库与⼀级缓存不同,⼆级缓存和具体... 查看详情

mybatis源码分析之05一级缓存

首先需要明白,mybatis的一级缓存就是指SqlSession缓存,Map缓步!通过前面的源码分析知道mybatis框架默认使用的是DefaultSqlSession,它是由DefaultSqlSessionFactory创建的,下面是源码privateSqlSessionopenSessionFromDataSource(ExecutorTypeexecType,Transaction... 查看详情

mybatis源码阅读之--本地(一级)缓存实现原理分析

前言:Mybatis为了提升性能,内置了本地缓存(也可以称之为一级缓存),在mybatis-config.xml中可以设置localCacheScope中可以配置本地缓存的作用域,包含两个值session和statement,其中session选项表示本地缓存在整个session都有效,而state... 查看详情

通过源码分析mybatis的缓存

  看了通过源码分析MyBatis的缓存这篇文章后,自己跟着源码过了一遍,对mybatis的一级缓存和二级缓存有了更清楚的认识。  一级缓存是SqlSession级别的,同一个sqlSession在第二次执行一个相同参数的select语句并且第一次执行... 查看详情

mybatis之二级缓存

 下面是二级缓存机制的样例:  查看详情

mybatis源码分析-缓存原理(代码片段)

...ache3.CacheKey4.一级缓存5.二级缓存6.总结参考序号内容链接1MyBatis源码分析-MyBatis入门https://thinkwon.blog.csdn.net/article/details/1148088522MyBatis源码分析-配置文件解析过程https://thinkwon.blog.csdn.net/article/details/1148089623MyBatis源码分析-映射文件... 查看详情

mybatis缓存之二级缓存

二级缓存(全局缓存):基于namespace级别的缓存,一个namespace对应一个二级缓存。工作机制:一个会话,查询一条数据,这条数据会放在当前会话的一级缓存中;如果会话关闭,该会话对应的一级缓存就消失了;可以使用二级缓... 查看详情

mybatis之缓存(代码片段)

目录一、简介     PerpetualCache增强的缓存功能分类二、原理1、PerpetualCache源码2、LRUCache,装饰器增强的缓存3、CacheKey4、一级缓存、二级缓存三、一级缓存访问&创建删除四、二级缓存开启命名空间划分访问&更新删除... 查看详情

从根上理解mybatis的一级二级缓存

1\\.写在前头这篇帖子主要讲一级缓存,它的作用范围和源码分析(本来想把一二级缓存合在一起,发现太长了)2\\.准备工作2.1两个要用的实体类publicclassDepartmentpublicDepartment(Stringid)this.id=id;privateStringid;/***部门名称*/privateStringname... 查看详情

源码级mybatis缓存策略(一级和二级缓存)(代码片段)

...们可以避免频繁的与数据库进行交互,进而提高响应速度MyBatis也提供了对缓存的支持,分为一级缓存和二级缓存,可以通过下图来理解:①、一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一... 查看详情

mybatis源码分析之@selectprovider注解使用详解(代码片段)

MyBatis源码分析之@SelectProvider注解使用详解之前讲了MyBatis的配置、plugin、Select查询,还有@MapKey注解的使用与原理,还有返回@ResultMap等等,我原想直接从MyBatis的缓存开始说起,但是想想还是得说一下MyBatis... 查看详情

mybatis一二级缓存的源码研究(代码片段)

Mybatis的一级缓存Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper... 查看详情

深入浅出mybatis的一级二级缓存机制(代码片段)

...及各种互联网高并发、高性能、高可用的解决方案。一、MyBatis缓存缓存就是内存中的数据,常常来自对数据库查询结果的保存。使用缓存,我们可以避免频繁与数据库进行交互,从而提高响应速度。MyBatis也提供了对... 查看详情

深入浅出mybatis的一级二级缓存机制(代码片段)

...及各种互联网高并发、高性能、高可用的解决方案。一、MyBatis缓存缓存就是内存中的数据,常常来自对数据库查询结果的保存。使用缓存,我们可以避免频繁与数据库进行交互,从而提高响应速度。MyBatis也提供了对... 查看详情

mybatis一级缓存,mybatis二级缓存,mybatis缓存失效(代码片段)

Mybatis一级缓存,Mybatis二级缓存,Mybatis缓存失效 ================================©Copyright蕃薯耀 2021-06-24https://www.cnblogs.com/fanshuyao/ 一、SpringBoot整合Mybatis1、pom.xml引入依赖(基于SpringBoot:2.3.12.REL 查看详情