MyBatis 二级缓存
MyBatis 二级缓存概述
-
MyBatis的二级缓存相对于一级缓存来说,实现了
SqlSession
之间缓存数据的共享,同时粒度更加的细,能够到namespace
级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。 -
MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
-
在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将 MyBatis的Cache 接口实现,有一定的开发成本,直接使用Redis、Memcached 等分布式缓存可能成本更低,安全性也更高。
MyBatis 二级缓存原理
一级缓存最大的共享范围就是一个 SqlSession
内部,如果多个 SqlSession
之间需要共享缓存,则需要使用到二级缓存。
开启二级缓存后,会使用 CachingExecutor
装饰 Executor
,进入一级缓存的查询流程前,先在CachingExecutor 进行二级缓存的查询。
二级缓存开启后,同一个 namespace
下的所有操作语句,都影响着同一个Cache。
每个 Mapper 文件只能配置一个 namespace,用来做 Mapper 文件级别的缓存共享。
<mapper namespace="mapper.StudentMapper"></mapper>
二级缓存被同一个 namespace
下的多个 SqlSession
共享,是一个全局的变量。MyBatis 的二级缓存不适应用于映射文件中存在多表查询的情况。
通常我们会为每个单表创建单独的映射文件,由于MyBatis的二级缓存是基于namespace
的,多表查询语句所在的namspace
无法感应到其他namespace
中的语句对多表查询中涉及的表进行的修改,引发脏数据问题。
MyBatis缓存查询的顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存。
二级缓存配置
开启二级缓存需要在 mybatis-config.xml 中配置:
<settingname="cacheEnabled"value="true"/>
二级缓存考题
测试update
操作是否会刷新该namespace
下的二级缓存。
开启了一级和二级缓存,通过三个SqlSession 查询和更新 学生张三的姓名,判断最后的输出结果是什么?
SqlSession sqlSession1 = factory.openSession(true);
SqlSession sqlSession2 = factory.openSession(true);
SqlSession sqlSession3 = factory.openSession(true);
StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
StudentMapper studentMapper3 = sqlSession3.getMapper(StudentMapper.class); System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
sqlSession1.commit();
System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1)); studentMapper3.updateStudentName("李四",1);
sqlSession3.commit();
System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));
答案:
张三
张三
李四
解答:三个 SqlSession 是共享 MyBatis 缓存,SqlSession2 更新数据后,MyBatis 的 namespace 缓存(StudentMapper) 就失效了,SqlSession2 最后是从数据库查询到的数据。