对mybatis一级二级缓存的源码级别的深刻理解

mybatis源码阅读

一,关于缓存 缓存分为一级缓存,二级缓存 一级缓存,二级缓存都默认开启
select标签中的flushCache默认是false,表示每次查询不清除缓存,然后将搜索数据放到一级缓存中名叫localCache的hashmap中。
以((命名空间+标签的id)+sql语句+sql参数+offset+limit
这后面两个默认值一般一样)经过计算封装成CacheKey对象作为key,数据作为集合

下图就是作为缓存中相同数据的依据了
在这里插入图片描述

开启二级缓存条件

1,mybatis-config.xml的配置文件中加上

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

默认开启的,不需要配置,下面这两个必须符合二级缓存才能开启

2,想开启二级缓存的mapper.xml配置文件中加上
<cache ></cache>

这两句话体现在下文CachingExecutor类中ms.getCache()得到的是否为null,配置了就不为null。

3该mapper.xml中用到的实体类必须实现Serializable接口,不然会报错
原因是SerializableCache中
在这里插入图片描述

因为会将你的数据放到缓存cache中,在放入的时候会进行instanceof判断你的实体类是否实现了Serializable接口,没实现就抛出异常,所以你想放入缓存的实体类必须实现该接口

4,标签中的userCache为true 体现在下文的CachingExecutor类中ms.isUseCache()这句话上。
这句话不知道作用看了下面就明白了

我们每次selectList()都会执行DefaultSqlSession这个类里面的selectList方法

在这里插入图片描述
执行到最下面的executor.query()方法时就会执行CachingExecutor里面的query()方法,
这里使用了装饰者设计模式对BaseExecutor进行了加强,类似于io流那块BufferedInputStream中的类,将BaseExecutor对象传入到CachingExecutor类中,从而达到包装增强

CachingExecutor里的代码

在这里插入图片描述
这里对调用createCacheKey方法生成CacheKey,继续向下执行,下面这个就是接下来执行的方法
在这里插入图片描述
在这里插入图片描述
首先获取ms.getCache,如果cache不为null,就代表你开启了二级缓存,
第一条红线flushCacheIfRequired(ms),其实就是每个增删改查标签的flushCache的值,如果为true就会清空二级缓存,然后在二级缓存中找 第二条红线,找到就返回,没找到就从数据库中的查找,如果你此时没有开启二级缓存,这里获取到的cache就是null,然后直接跳到最后一句,最后一句的作用就是走BaseExecutor中的方法;
再将查找的结果放入到内存中还没有真正放入二级缓存。二级缓存对象hashmap对象存放在tcm(TransactionCacheManager类中)存放。
在这里插入图片描述
在这里插入图片描述
开启二级缓存后,每次查询到的数据先放到transactionalCaches集合中的transactionalCache的属性entriesToAddOnCommit集合中暂时存放。

二级缓存里的对象的鸟瞰图,真正存放二级缓存数据的地方(下图)

在这里插入图片描述
本质上是装饰器模式的使用,具体的执行链是
SynchronizedCache -> LoggingCache -> SerializedCache -> LruCache -> PerpetualCache。

二级缓存放入时的debug图,这时还没有真正放入可查询的二级缓存中,只有当sqlSession关闭后或者提交后才可以将当前内存里的(即上图的entriesToOnCommit中的ArrayList利用序列化写入到真正查找二级缓存中的地方)

## 二级缓存放入时的debug图,这时还没有真正放入可查询的二级缓存中,只有当sqlSession关闭后或者提交后才可以将当前内存里的(即上图的entriesToOnCommit中的ArrayList数据利用序列化写入到真正查找二级缓存中的地方)
如果你不提交不关闭的话,就不会在二级缓存中查找,如果一级缓存也没有就走数据库了

在这里插入图片描述
划红线的就是将保存在内存里的数据进行序列化写入到真正的二级缓存中,下图就是entriesToOnCommit中的数据序列化成字节写入到灰色delegate是个LruCache中的value中,这是才是真正的二级缓存
在这里插入图片描述

BaseExecutor中的代码

在这里插入图片描述
第一个划红线的就是中的flushCache的值,如果为true就会清空一级缓存。
在这里插入图片描述
localCache其实就相当于hashmap,将里面的数据清空
select默认是false,insert,delete,update默认是true。
然后就到我们的第二个红线的位置,这句话的意思就是在一级缓存hashmap中根据前面生成的CacheKey去找,如果为空就说明缓存中没有,此时就会执行我们的第三个划红线的地方,见名知意,queryFromDatabase()就是从数据库中查找我们想要的数据
在这里插入图片描述
第一个划红线的就是真正执行查询数据库中的数据
第二个划红线的就是将数据放到一级缓存中的hashmap中以cacheKey为key数据为value进行存储

总结

CachingExecutor,获取mappedStatement的cache,------------->
1,如果mapper.xml配置了标签开启了cache就不为null,然后进行判断是否flushCache,为true就将标志位clearOnCommit=true(等最后提交或者关闭sqlSession再真正放入二级缓存),去二级找,找到返回,没找到去一级,然后数据库,放入缓存,提交关闭清除缓存 ---------->
2,然后判断userCache(查询标签上的)是否为true,为true就到二级缓存中去查找,找到就将数据返回结束;
userCache为false直接去一级缓存,没找到就去数据库,将数据放入一级缓存,不放入二级缓存,返回数据结束---------------->
BaseExecutor中的查找,首先查看flushCache是否为true,如果为true就清空一级缓存,然后在一级缓存里找(清空了肯定找不到)然后到数据库中查找,然后将数据放入到一级缓存中后将数据返回结束。

这就是一级缓存二级缓存的所有东西。

下面这些标签就是自带的默认值,flushCache为true会清空二级一级缓存 useCache代表开启二级缓存

<select flushCache=”false” useCache=”true”></select>
<insert flushCache=”true” useCache=”false”></isnert>
<delete flushCache=”true” useCache=”false”></delete>
<update flushCache=”true” useCache=”false”></update>

好了到这里我们就基本把mybatis的一级二级缓存看完了,一级缓存是针对一个sqlSession范围的,只是一个用户在一个sqlSession内的两次相同的查询才回到一级缓存,这样就有点鸡肋;
二级缓存是相当于一个mapper.xml级别的,一个命名空间的都可以共享二级缓存,也就是说所有用户只要是调用一个mapper.xml中的查询语句都可以到二级缓存中去查找,但只要进行增删改了就会清除二级缓存。

二级缓存的缺点:

二级缓存最好在单表操作的那个mapper.xml时开启,多表关联查询最好不开,因为一个命名空间修改了相关联表的数据,这个命名空间的数据的二级缓存数据失效,而别的命名空间还是生效的,但你是关联表查询,你在数据中修改了别的命名空间用到的数据,这时你在别的命名空间查询跟刚才修改数据时的那个数据有关系时,读时有可能就是脏数据了,这样可能造成一些未知的后果。

二级缓存失效情况:

如果开启事务后(默认开启的,每次查询增删改操作时,都会在方法里面开启事务)增删改后,不会立即让二级缓存失效,因为每次增删改后就修改clearOnCommit
= true;等commit或者关闭sqlSession时才会根据这个标志判断是否为truel来清除二级缓存。 查询数据也是,不会立马将数据放入到二级缓存,而是先放入暂时内存中,等最后提交或者close
SqlSession时才会将暂时内存中的数据再序列化放入到二级缓存中,一级缓存则直接放入。

我们来看看update()更新方法

在这里插入图片描述
在这里插入图片描述
会根据你配置的flushCache来决定是否清除二级缓存,update,insert默认为true
在这里插入图片描述
在这里插入图片描述
然后清除一级缓存,这里无论怎样都会清除一级缓存,清除一级缓存的代码是写死的 增删改的代码在里面调用的都是该update方法。
增删改标签的useCache是个摆设,无论true还是false都没啥影响

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值