MyBatis 二级缓存实现详解及使用注意事项
二级缓存介绍
在上文中提到的一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。

二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。
当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
二级缓存配置
要正确的使用二级缓存,需完成如下配置的。
首先,在MyBatis的配置文件中开启二级缓存。
<setting name="cacheEnabled" value="true"/>
然后,在MyBatis的 Mapper XML 中加入 cache 或者 cache-ref 标签。
cache标签用于声明这个namespace需要使用二级缓存,并且可以自定义配置。
<cache/>
type:cache使用的类型,默认是PerpetualCache,这在一级缓存中提到过。eviction: 定义回收的策略,常见的有FIFO,LRU。flushInterval: 配置一定时间自动刷新缓存,单位是毫秒。size: 最多缓存对象的个数。readOnly: 是否只读,若配置可读写,则需要对应的实体类能够序列化。blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
cache-ref代表引用别的命名空间的Cache配置,两个命名空间的操作使用的是同一个Cache。
<cache-ref namespace="mapper.StudentMapper"/>
二级缓存实验
public void testCacheWithoutCommitOrClose() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务
StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
System.out.println(studentMapper.getStudentById(1)); // 查询数据库
System.out.println(studentMapper2.getStudentById(1)); // 查询数据库,二级缓存不起作用
}
public void testCacheWithCommitOrClose() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务
StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
System.out.println(studentMapper.getStudentById(1)); // 查询数据库
sqlSession1.commit(); // 提交事务
System.out.println(studentMapper2.getStudentById(1)); // 查询缓存,二级缓存起作用
}
public void testCacheWithUpdate() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession3 = factory.openSession(false); // 不自动提交事务
StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
StudentMapper studentMapper3 = sqlSession3.getMapper(StudentMapper.class);
System.out.println(studentMapper.getStudentById(1)); // 查询数据库
sqlSession1.commit(); // 提交事务
System.out.println(studentMapper2.getStudentById(1)); // 查询缓存,二级缓存起作用
studentMapper3.updateStudentName("方方",1); // 更新数据
sqlSession3.commit(); // 提交事务
System.out.println(studentMapper2.getStudentById(1)); // 查询数据库,说明清除了二级缓存
}
public void testCacheWithDiffererntNamespace() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession3 = factory.openSession(false); // 不自动提交事务
StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
ClassMapper classMapper = sqlSession3.getMapper(ClassMapper.class);
System.out.println(studentMapper.getStudentByIdWithClassInfo(1)); // 关联查询数据库
sqlSession1.close();
System.out.println(studentMapper2.getStudentByIdWithClassInfo(1)); // 查询缓存,二级缓存起作用
classMapper.updateClassName("特色一班",1); // 更新其中一个表的数据
sqlSession3.commit(); // 提交更新事务
System.out.println(studentMapper2.getStudentByIdWithClassInfo(1)); // 仍然查询缓存,引起了脏读
}
// 让ClassMapper引用StudenMapper的命名空间,这样两个映射文件对应的SQL操作都会使用同一块缓存
public void testCacheWithDiffererntNamespace() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession3 = factory.openSession(false); // 不自动提交事务
StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
ClassMapper classMapper = sqlSession3.getMapper(ClassMapper.class);
System.out.println(studentMapper.getStudentByIdWithClassInfo(1)); // 关联查询数据库
sqlSession1.close();
System.out.println(studentMapper2.getStudentByIdWithClassInfo(1)); // 查询缓存,二级缓存起作用
classMapper.updateClassName("特色一班",1); // 更新其中一个表的数据
sqlSession3.commit(); // 提交更新事务
System.out.println(studentMapper2.getStudentByIdWithClassInfo(1)); // 关联查询数据库,二级缓存失效
}
总结
- MyBatis的二级缓存相对于一级缓存来说,实现了
SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。 - MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
- 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。
参考:https://mybatis.org/mybatis-3/zh/configuration.html#settings
http://mybatis.org/spring/zh/factorybean.html
https://tech.meituan.com/2018/01/19/mybatis-cache.html
https://blog.csdn.net/u010349169/article/details/41408341
https://blog.csdn.net/mengsofts/article/details/88074790
MyBatis 二级缓存实现详解及使用注意事项的更多相关文章
- MyBatis 二级缓存全详解
目录 MyBatis 二级缓存介绍 二级缓存开启条件 探究二级缓存 二级缓存失效的条件 第一次SqlSession 未提交 更新对二级缓存影响 探究多表操作对二级缓存的影响 二级缓存源码解析 二级缓存 ...
- MyBatis 一级缓存实现详解及使用注意事项
一级缓存介绍 在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对 ...
- MyBatis 一级缓存、二级缓存全详解(一)
目录 MyBatis 一级缓存.二级缓存全详解(一) 什么是缓存 什么是MyBatis中的缓存 MyBatis 中的一级缓存 初探一级缓存 探究一级缓存是如何失效的 一级缓存原理探究 还有其他要补充的 ...
- 《深入理解mybatis原理6》 MyBatis的一级缓存实现详解 及使用注意事项
<深入理解mybatis原理> MyBatis的一级缓存实现详解 及使用注意事项 0.写在前面 MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓 ...
- 《深入理解mybatis原理》 MyBatis的一级缓存实现详解 及使用注意事项
MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓存机制上.MyBatis提供了一级缓存.二级缓存 这两个缓存机制,能够很好地处理和维护缓存,以提高系统的性能.本 ...
- mybatis深入理解(五)-----MyBatis的一级缓存实现详解 及使用注意事项
0.写在前面 MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓存机制上.MyBatis提供了一级缓存.二级缓存 这两个缓存机制,能够很好地处理和维护缓存,以提高 ...
- Hibernate中一级缓存和二级缓存使用详解
一.一级缓存二级缓存的概念解释 (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个 session(一定要同一个ses ...
- Spring 整合 Hibernate 时启用二级缓存实例详解
写在前面: 1. 本例使用 Hibernate3 + Spring3: 2. 本例的查询使用了 HibernateTemplate: 1. 导入 ehcache-x.x.x.jar 包: 2. 在 a ...
- Mybatis SQL映射文件详解
Mybatis SQL映射文件详解 mybatis除了有全局配置文件,还有映射文件,在映射文件中可以编写以下的顶级元素标签: cache – 该命名空间的缓存配置. cache-ref – 引用其它命 ...
随机推荐
- uni-app使用腾讯地图注意点
地图map组件使用腾讯地图自定义样式: 1:在使用地图map组件腾讯地图时,获取本地定位,经纬度转地址与地址转经纬度解析时,小程序可以直接使用.但是h5版本会报跨域问题,目前前端没有找到更好的解决方法 ...
- [BUUCTF]REVERSE——rsa
rsa 附件 题目是rsa,首先了解一下什么是rsa加密,具体的看这个文章 首先大概介绍下RSA加密解密 RSA加密是对明文的E次方后除以N后求余数的过程 公钥n = p * q,其中p和q是两个大素 ...
- 直接在filter过滤器代码里加org.apache.struts2.ServletActionContext.getRequest()会出现空指针情况
直接在filter过滤器代码里加org.apache.struts2.ServletActionContext.getRequest()获得request对象请注意啦,会出现空指针情况 请关注此处:
- 解决VMware开机黑屏问题
最近搞虚拟机.一直遇见了许多问题.发现某一天.VMware开机后.其中一个虚拟机直接卡住黑屏了.关的时候就一直显示.xxx繁忙.无法关闭.网上搜到了解决方法.这边记录一下. 1.关闭所有服务 2.修复 ...
- MacOS设置终端代理
前言 国内的开发者或多或少都会因为网络而烦恼,因为一些特殊原因有时候网络不好的时候需要使用代理才能完成对应的操作.原来我一直都是使用斐讯路由器然后刷了梅林的固件,直接在路由器层面设置转发代理,把一些国 ...
- UDP&串口调试助手用法(5)
note 提供安装包 基于win10开发 已通过win10测试,windows其他平台,没有测试 日志 2021-09-18 1.修复计算校验和错误的现象 2.屏蔽不计算校验和位置的REG验证(后期更 ...
- Mac下好用的“visio”之 OmniGraffle Pro
!!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist 1.官方网站:https://www.omnigroup.com/omnigraffle/ ...
- c++设计模式概述之工厂
类写的不规范,原因: 缩短篇幅,实际中请不要这样写. 欢迎指正 工厂模式,如其名,想象下现实生活中的工厂,比如Apple的组装工厂,小米产品的组装工厂,华为设备的组装工厂.对我们用户而言,不需要知道他 ...
- 【LeetCode】316. Remove Duplicate Letters 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- Robin Hood
Robin Hood 题目链接 题意 给你n个人和他们的钱数,然后给你k天,每天可以从最高钱数的人那边取一块钱给最少钱数的人,问最后钱数最多的人和钱数最少的人相差多少: 思路 二分最钱数,能下降到的位 ...