当前位置:   article > 正文

Mybatis源码学习(十):二级缓存_mybatis二级缓存源码

mybatis二级缓存源码

一、前文回顾

在前一篇文章中学习了Mybatis的一级和二级缓存,了解了一级缓存针对的是单个SqlSession而二级缓存则可以在多个SqlSession中共享,所以我们通常也称其为全局缓存。今天我们继续学习Mybatis的二级缓存。

二、思考

public static void main(String[] args) throws Exception {
    String resource = "mybatis-config.xml";
    //读取mybatis配置文件并获取输入流
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession session = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession()) {
        PersonMapper mapper = session.getMapper(PersonMapper.class);
        //先查询一次
        Person person = mapper.selectById(1L);
        System.out.println(person);
        session.commit();
        //换个SqlSession再查一次
        PersonMapper mapper1 = session2.getMapper(PersonMapper.class);
        mapper1.selectById(1L);
        System.out.println(person);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

1、二级缓存是什么类

1、在探究二级缓存是如何创建之前,我们先要知道二级缓存是什么(这里说的是什么,指的是什么类),紧接着上一篇文章的的源码,我们可以看到Mybatis是从tcm这个对象(类是TransactionCaches:事务缓存),我们Debug一下这个类。
可以看出来这是一个很典型的装饰器设计模式,特点一层嵌套一层。可以看到最底层就是我们熟悉PerpetualCache,同时在此基础上又包装上了LruCache(提供了缓存换出功能)、然后包装了(SerializedCache:序列化缓存,所以如果想使用二级缓存,被缓存的对象一定要实现序列化接口)等等。

image.png
所以这个问题的答案就很明显了,二级缓存的底层数据结构为HashMap,由一个名为PerpetualCache的类封装,并在此基础上添加了一系列的装饰器。

2、二级缓存是什么时候创建的


知道了二级缓存是什么,接下来我们看他是如何被创建的。不知道你是否还记得之前写的文章里介绍了我们Configuration,也就是Mybatis的配置类,在那一篇文章中我们讲述了Mybatis是如何把我们编写的Xml封装成Configuration的对象(如果忘记了,可以回头瞄一眼)。因为Configuration中包含有Cache相关属性,所以我们推断Cache应该是同Configuration一起创建的。在XMLMapperBuilder中有这么一个方法,用于解析Cache
image.png
继续Debug该方法,可以看出Mybatis通过BuilderAssistant类构建了一个Cache对象
image.png 哦了,继续Debug
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);这个方法
image.png
这里的代码也比较容易理解,就是通过建造者设计模式来构建一个Cache,并且添加到Configuration对象中。至此我们已经知道了Mybatis的二级缓存是如何被创建的了。

3、Mybatis中二级缓存和一级缓存的顺序(开启状态)

在一开始看到这个问题时,我毫不犹豫的认为一级缓存是优先于二级缓存执行的(毕竟一级之后才二级),但是在分析过源码之后才发现这样的认识是错误的,很多时候我们不能凭感觉去猜测,更多的是要结合源码。
image.png
源码分析:可以看到当我们开启二级缓存的时候,查询一开始会进入CachingExecutor的Query方法,如果能直接命中缓存则从缓存中返回数据,否则会通过委托类(这里是BaseExecutor)进行Query,接下来我们进入BaseExecutor类中。
image.png
这个代码就太熟悉了,这里不做过多介绍。所以得出结论,当二级缓存生效的情况下,Mybatis会先查询二级缓存如果二级缓存没有命中,则再去查询一级缓存。

4、为什么说二级缓存是全局缓存

因为二级缓存可以被多个SqlSession共享,话不多说直接Debug。
我们第一个查询,MappedStatment是@1738,此时Cache为@1749
image.png
第二个MappedStatment@1738,此时Cache也是@1749
image.png
所以可以说明对于多个SqlSession用的是同一个Cache。

5、多个Mapper是否可以共享一个Cache

这里直接给出答案,可以。
image.png

三、二级缓存结构详解

image.png
借用上面的图,可以看出二级缓存最终是封装了PerpetualCache,而PerpetualCache的底层又是HashMap。
keycom.cmxy.mapper.PersonMapper也就是我们所写的Mapper文件的nameSpace。(这又可以证明上面的问题,多个mapper是可以共用同一个Cache的,只要设置对应的nameSpace即可)而Value则是一个名为Cache的对象。我们继续看这个Cache对象
image.png
该Cache也是一个类似于Map的结构,其中key
-312300504:-769787714:com.cmxy.mapper.PersonMapper.selectById:0:2147483647:select * from t_person where id = ?:1:development
分解一下就是
multiplier:乘法因子,默认37
hashCode:哈希值,本例中是 -312300504
checkSum:表示所有更新对象baseHashCode的和 ,本例中是-769787714
updateList:本次查询相关参数; nameSpace+方法名+分页参数+具体SQL+SQL参数+环境(这里是develop,对应配置文件里的environment节点)
Value:是一个byte数组,也就是我们查询出来放在缓存中的对象序列化之后的产物。

四、总结

今天我们进一步的学习了Mybatis的二级缓存,下一篇文章将会继续探究Mybatis的二级缓存。当然也是二级缓存的结束篇,希望对你有所帮助,

未完待续

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/blog/article/detail/57284
推荐阅读