23、mybatis学习——mybatis的二级缓存
MyBatis 一级缓存最大的共享范围就是一个SqlSession内部,那么如果多个 SqlSession 需要共享缓存,则需要开启二级缓存,开启二级缓存后,会使用 CachingExecutor 装饰 Executor,进入一级缓存的查询流程前,先在CachingExecutor 进行二级缓存的查询,具体的工作流程如下所示
当二级缓存开启后,同一个命名空间(namespace) 所有的操作语句,都影响着一个共同的 cache,也就是二级缓存被多个 SqlSession 共享,是一个全局的变量。当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
二级缓存开启条件
二级缓存默认是不开启的,需要手动开启二级缓存,实现二级缓存的时候,①MyBatis要求返回的POJO必须是可序列化的。开启二级缓存的条件也是比较简单,通过直接②在 MyBatis 配置文件中通过
<settings> <setting name = "cacheEnabled" value = "true" /> </settings>
来开启二级缓存,③还需要在 Mapper 的xml 配置文件中加入 <cache>
标签
设置 cache 标签的属性
cache 标签有多个属性
eviction
: 缓存回收策略,有这几种回收策略- LRU - 最近最少回收,移除最长时间不被使用的对象(默认)
- FIFO - 先进先出,按照缓存进入的顺序来移除它们
- SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象
flushinterval
缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一个毫秒值readOnly
: 是否只读;true 只读,MyBatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。读写(默认):MyBatis 觉得数据可能会被修改size
: 缓存存放多少个元素type
: 指定自定义缓存的全类名(实现Cache 接口即可)blocking
: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
举例:
Student.java(实现了序列化接口)
StudentMapper接口和StudentMapper.xml
mybatis全局配置文件中开启二级缓存
在StudentMapper.xml开启二级缓存
<!-- namespace中开启二级缓存 eviction:缓存的回收策略(默认是LRU) LRU - 最近最少回收,移除最长时间不被使用的对象 FIFO - 先进先出,按照缓存进入的顺序来移除它们 SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象 WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象 flushInterval:缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一个毫秒值 readOnly:是否只读;true 只读,MyBatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。 false 非只读(默认):MyBatis觉得获得的数据可能会被修改;mybatis会利用序列化&反序列化的技术克隆一份新的数据给你;安全,速度稍慢 size:缓存存放多少个元素 type: 指定自定义缓存的全类名(实现Cache接口即可) blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。 --> <cache eviction="LRU" flushInterval="60000" readOnly="false"></cache>
测试方法
//测试二级缓存 @Test public void testSecondLevelCache() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //两个会话 SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class); StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class); //会话1关闭,此时student1从一级缓存转移到二级缓存 Student student1 = studentMapper1.getStuById(1); sqlSession1.close(); //会话2查询id为1的student,二级缓存中有,直接取出 Student student2 = studentMapper2.getStuById(1); sqlSession2.close(); //student2从二级缓存中取出时,若<cache>中的属性readeronly为true //则这个输出true,若为false则这里输出false System.out.println(student1==student2); }
测试结果
此时在StudentMapper.xml中修改配置;增加一个useCache="false"(<select>标签中才有的属性)
则此条查询语句不使用二级缓存;及时全局配置文件和当前namespace配置文件开启了二级缓存
同样进行测试
总结:
和二级缓存有关的设置/属性:
1、需要使用二级缓存的POJO实现序列化接口Serializable
2、<setting name="cacheEnabled" value="true"/>
3、<cache></cache>
4、<select>标签中的useCache属性
5、mapper.xml中的每一个增删改标签中的flushCache属性,默认为true;执行了增删改操作(一级和二级缓存都会清空)