在上文中提到的一级缓存中,其最大的共享范围就是一个 SqlSession 内部,如果多个 SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用 CachingExecutor 装饰Executor,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询,具体的工作流程如下所示。
二级缓存开启后,同一个 namespace 下的所有操作语句,都影响着同一个 Cache,即二级缓存被多个 SqlSession 共享,是一个全局的变量。
当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
二级缓存配置要正确的使用二级缓存,需完成如下配置的。
在MyBatis的配置文件中开启二级缓存。
<setting value="true"/>在 MyBatis 的映射 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"/> 二级缓存实验接下来我们通过实验,了解 MyBatis 二级缓存在使用上的一些特点。
在本实验中,id 为1的学生名称初始化为点点。
实验1测试二级缓存效果,不提交事务,sqlSession1 查询完数据后,sqlSession2 相同的查询是否会从缓存中获取数据。
@Test public void testCacheWithoutCommitOrClose() throws Exception { SqlSession sqlSession1 = factory.openSession(true); SqlSession sqlSession2 = factory.openSession(true); StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class); StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class); System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1)); System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1)); }执行结果:
我们可以看到,当 sqlsession 没有调用 commit() 方法时,二级缓存并没有起到作用。
实验2测试二级缓存效果,当提交事务时,sqlSession1 查询完数据后,sqlSession2 相同的查询是否会从缓存中获取数据。
@Test public void testCacheWithCommitOrClose() throws Exception { SqlSession sqlSession1 = factory.openSession(true); SqlSession sqlSession2 = factory.openSession(true); StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class); StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class); System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1)); sqlSession1.commit(); System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1)); }从图上可知,sqlsession2 的查询,使用了缓存,缓存的命中率是0.5。
实验3测试 update 操作是否会刷新该 namespace 下的二级缓存。
@Test public void testCacheWithUpdate() throws Exception { 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)); }