深入理解MyBatis缓存——源码分析

1. 前言

使用缓存可以使应用更快地获取数据,避免频繁的数据库交互,尤其是在查询越多、缓存命中率越高的情况下,使用缓存的作用就越明显。MyBatis 作为持久化框架,提供了非常强大的查询缓存特性,可以非常方便地配置和定制使用
如下图所示:a1和a2引用的是同一个对象
在这里插入图片描述

MyBatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对象中。如果某次查询中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map缓存对象中已经存在该键值时,则会返回缓存中的对象

2. 一级缓存

一级缓存(也叫本地缓存,localCache)默认会启用
存在于SqlSession(这里的session指的是DefaultSqlSession,而不是SqlSessionTemplate)的生命周期中。

2.1 直接使用sqlSession获取Dao

这种方式Dao中的sqlSession为DefaultSqlSession,而不是SqlSessionTemplate
如下图所示:
在这里插入图片描述
MapperMethod中的sqlSession就是firstLevelCacheTest()中的sqlSession
在这里插入图片描述
查询数据时,先生成相应的key,再从localCache中获取结果,从缓存中取值的代码在BaseExecutor中的query()方法中
在这里插入图片描述
在这里插入图片描述
将结果放入缓存
在这里插入图片描述

2.2 清除缓存

2.2.1 任何的 insert、update、delete操作都会清空缓存,这三类语句都会调用update(),然后再让executor执行,最终会在executor中清空缓存

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
执行update语句前,先回调用clearLocalCache()方法清空缓存
在这里插入图片描述

2.1.2 配置flushCache="true"也会清空缓存

<select id="selectXXX" flushCache="true" resultMap="xxxMap">

在查询数据前清空当前的一级缓存。这个配置也会清空二级缓存,下面会提到
在这里插入图片描述

2.3 MyBatis在Spring中的一级缓存

在Spring中,自动注入dao,dao中的sqlSession为sqlSessionTemplate,所以每次执行时需要获取DefaultSqlSession
在这里插入图片描述
开启事务后,同一事务是同一个SqlSession,所以缓存不失效。
第一次查询时将SqlSession绑定到事务中,后面的查询直接从事务管理器中获取SqlSession
在这里插入图片描述
不开启事务,每查询获取新的SqlSession,所以缓存会失效
在这里插入图片描述

2.4 缓存范围

一级缓存的范围有两种,statement和session。配置如下

mybatis:
  configuration:
    local-cache-scope: statement

默认是session。如果缓存范围是statement,会清空缓存
在这里插入图片描述
在这里插入图片描述

3. 二级缓存

MyBatis的二级缓存是和命名空间绑定的,即二级缓存需要配置在mapper.xml映射文件中,或者配置在 mapper.java 接口中。在映射文件中,命名空间就是 XML 根节点 mapper 的namespace属性。在mapper接口中,命名空间就是接口的全限定名称
开启二级缓存后,返回值需要实现序列化接口,否则会报错

3.1 配置

全局开关, 默认为true, 所以可以不用配置
mybatis-config.xml

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

springboot中配置

mybatis:
  configuration:
    cache-enabled: true

在全局开关打开的情况下,需要在命名空间中开启
mapper.xml中开启

<cache/>	

在mapper.java接口中开启

@CacheNamespace(eviction = LruCache.class,flushInterval = 0L,size = 1024, readWrite = true)
public interface ADao extends CommonMapper<A> {
}

注解中的值可以不添加,用默认值。

3.2 不使用缓存

在命名空间缓存开启的情况下,可以通过设置 useCache="false"来让某个方法不使用缓存
注意:这里不会清空缓存,只是查询不使用缓存。

<select id="getById" useCache="false" resultType="com.shopping.demo24.model.A">
    select * from t_a where id = #{id}
</select>

当缓存不为空时,判断useCache属性的值是否为true,如果是true就从缓存中获取。
在这里插入图片描述

3.3 从缓存中获取结果

从MappedStatement中获取缓存

List<E> list = (List<E>) tcm.getObject(cache, key);

不同的查询,ms始终是一个对象
**在这里插入图片描述**
如果开启事务,tcm就是同一个对象,但不影响缓存的获取,因为缓存是在cache中,tcm只是将cache对象缓存在自己本身的对象里而已。实际的缓存是在ms.getCache()中

3.4 缓存结果

tcm.putObject(cache, key, list)

先将结果放入TransactionalCache中的entriesToAddOnCommit中
在这里插入图片描述
提交的时候再将结果刷到缓存
在这里插入图片描述

3.5 清除缓存

3.5.1 配置flushCache="true"清除缓存

如果statement配置flushCache=“true”,则会在查询前清除二级缓存

<select id="getById" flushCache="true" resultType="com.shopping.demo24.model.A">
    select * from `t_a` where id = #{id}
</select>

在这里插入图片描述
在这里插入图片描述
如果没有配置flushCache=“true”。select对应的statement中的flushCacheRequired为false,所以不会清空
在这里插入图片描述

3.5.2 update语句

在不配置flushCache="true"的情况下,update(包含insert和delete) flushCacheRequired的值为true,所以会清空缓存
在这里插入图片描述
先将clearOnCommit设为true
在这里插入图片描述
再在提交时清空缓存
在这里插入图片描述

3.6 缓存替换策略

MappedStatement中缓存淘汰策略默认用的是LRU
在这里插入图片描述

4. 缓存的命中顺序

缓存命中顺序:二级缓存-> 一级缓存-> 数据库
在这里插入图片描述
在这里插入图片描述
再次注意:flushCache="true"会清空一级和二级缓存

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值