MyBatis 缓存的使用,看这篇就够了

本文深入探讨了MyBatis的一级和二级缓存机制,包括一级缓存的工作原理、不同SqlSession之间的行为,以及如何刷新缓存。二级缓存则涉及到配置、实体序列化接口实现及使用案例,揭示了SqlSessionFactory生命周期中的缓存共享。通过对缓存的了解,有助于避免脏数据问题,提高应用性能。
摘要由CSDN通过智能技术生成

在日常工作中,我们大多使用MyBatis的默认缓存配置,但是MyBatis缓存机制也有一些不足之处,一不留神就会出现脏数据,形成一些潜在的隐患,后续排查问题容易浪费时间精力。

所以,如何用好MyBatis的缓存,是重中之重的。这里主要介绍MyBatis 的一级缓存和二级缓存。

在默认的情况下, 只开启一级缓存(一级缓存是对同一个 SqlSession 而言的)。

1 一级缓存

同一个 SqlSession 对象, 在参数和 SQL 完全一样的情况先, 只执行一次 SQL 语句(如果缓存没有过期) 也就是只有在参数和
SQL 完全一样的情况下, 才会有这种情况。

原理图如下:
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个sqlSession中,不同的sqlSession中的缓存是互相不能读取的。

在这里插入图片描述

1.1 同一个 SqlSession

在这里插入图片描述

在以上的代码中, 进行了两次查询, 使用相同的 SqlSession, 结果如下

在日志和输出中:
第一次查询发送了 SQL 语句, 后返回了结果;
第二次查询没有发送 SQL 语句, 直接从内存中获取了结果。
而且两次结果输入一致, 同时断言两个对象相同也通过。

1.2 不同的 SqlSession

在这里插入图片描述

在代码中, 分别使用 sqlSessionsqlSession2 进行了相同的查询。
其结果如下
在这里插入图片描述

从日志中可以看到两次查询都分别从数据库中取出了数据。 虽然结果相同, 但两个是不同的对象。

1.3 刷新缓存

刷新缓存是清空这个 SqlSession 的所有缓存, 不单单是某个键。
在这里插入图片描述

如果是以上, 没什么不同, 结果还是第二个不发 SQL 语句。
在此, 做一些修改, 在 StudentMapper.xml 中, 添加
在这里插入图片描述

修改后的配置文件如下:
在这里插入图片描述

结果如下:
在这里插入图片描述

第一次, 第二次都发送了 SQL 语句, 同时, 断言两个对象相同出错。

1.4 总结

在同一个 SqlSession 中, MyBatis 会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据;

不同的 SqlSession 之间的缓存是相互隔离的;

用一个 SqlSession, 可以通过配置使得在查询前清空缓存;

任何的 UPDATE, INSERT, DELETE 语句都会清空缓存。

2 二级缓存

二级缓存存在于 SqlSessionFactory 生命周期中。二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。
在这里插入图片描述

2.1 配置二级缓存

2.1.1 全局开关

在 MyBatis 中, 二级缓存有全局开关和分开关, 全局开关, 在 MyBatis-config.xml 中如下配置:
在这里插入图片描述

默认是为 true, 即默认开启总开关。

2.1.2 分开关

分开关就是说在 *Mapper.xml 中开启或关闭二级缓存, 默认是不开启的。

2.1.3 entity 实现序列化接口

2.2 使用二级缓存

在这里插入图片描述

结果如下:
在这里插入图片描述

以上结果, 分几个过程解释:
第一阶段:

在第一个 SqlSession 中, 查询出 student 对象, 此时发送了 SQL 语句;

student更改了name 属性;

SqlSession 再次查询出 student1 对象, 此时不发送 SQL 语句, 日志中打印了 「Cache Hit Ratio」, 代表二级缓存使用了, 但是没有命中。 因为一级缓存先作用了。

由于是一级缓存, 因此, 此时两个对象是相同的。

调用了 sqlSession.close(), 此时将数据序列化并保持到二级缓存中。

第二阶段:

新创建一个 sqlSession.close() 对象;

查询出 student2 对象,直接从二级缓存中拿了数据, 因此没有发送 SQL 语句, 此时查了 3 个对象,但只有一个命中, 因此 命中率 1/3=0.333333;

查询出 student3 对象,直接从二级缓存中拿了数据, 因此没有发送 SQL 语句, 此时查了 4 个对象,但只有一个命中, 因此 命中率 2/4=0.5;

由于 readOnly=“false”, 因此 student2 和 student3 都是反序列化得到的, 为不同的实例。

看到这里的小伙伴,如果你喜欢这篇文章的话,别忘了转发、收藏、留言互动!

如果对文章有任何问题,欢迎在留言区和我交流~

最近我新整理了一些资料,包含面经分享、模拟试题、和视频干货,如果你需要的话,欢迎私信我!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值