Mybatis存在一级缓存和二级缓存,默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存(一级缓存无法关闭,只能调整)
- public static void main(String[] args) throws InterruptedException {
- try (SqlSession sqlSession = MybatisUtil.getSession(true)){
- TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
- Student student1 = testMapper.getStudentBySid(1);
- Student student2 = testMapper.getStudentBySid(1);
- System.out.println(student1 == student2);
- }
- }
两次得到的是同一个Student对象,也就是说我们第二次查询并没有重新去构造对象,而是直接得到之前创建好的对象
- public static void main(String[] args) throws InterruptedException {
- try (SqlSession sqlSession = MybatisUtil.getSession(true)){
- TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
- Student student1 = testMapper.getStudentBySid(1);
- testMapper.addStudent(new Student().setName("小李").setSex("男"));
- Student student2 = testMapper.getStudentBySid(1);
- System.out.println(student1 == student2);
- }
- }
一级缓存,在进行DML操作后,会使得缓存失效,也就是说Mybatis知道我们对数据库里面的数据进行了修改,所以之前缓存的内容可能就不是当前数据库里面最新的内容了。还有一种情况就是,当前会话结束后,也会清理全部的缓存,因为已经不会再用到了。但是一定注意,一级缓存只针对于单个会话,多个会话之间不相通。
注意:一个会话DML操作只会重置当前会话的缓存,不会重置其他会话的缓存,也就是说,其他会话缓存是不会更新的!
- <cache
- eviction="FIFO"
- flushInterval="60000"
- size="512"
- readOnly="true"/>
一级缓存给我们提供了很高速的访问效率,但是它的作用范围实在是有限,如果一个会话结束,那么之前的缓存就全部失效了,但是我们希望缓存能够扩展到所有会话都能使用,因此我们可以通过二级缓存来实现,二级缓存默认是关闭状态,要开启二级缓存,我们需要在映射器XML文件中添加
- <mapper namespace="edu.test.mapper.TestMapper">
- <cache
- eviction="FIFO"
- flushInterval="60000"
- size="512"
- readOnly="true"/>
- <resultMap id="test" type="Student">
- <result property="name" column="sname"/>
- <result column="sno" property="sno"/>
- <result column="sdept" property="dept"/>
- <result property="sex" column="ssex"/>
- resultMap>
-
-
- <select id="selectMaleStudent" resultMap="test">
- select * from student where ssex='男';
- select>
-
-
- mapper>
- public static void main(String[] args) {
- try (SqlSession sqlSession = MybatisUtil.getSqlSession(true)) {
- SqlSession sqlSession1=MybatisUtil.getSqlSession(true);
- TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
- TestMapper testMapper1 = sqlSession1.getMapper(TestMapper.class);
-
- Student student1 = testMapper1.getStudent("20200512039");
-
- sqlSession1.close();
- Student student = testMapper.getStudent("20200512039");
-
- System.out.println(student == student1);
- System.out.println(student.equals(student1));
-
- }
- }
输出
- 开始构造
- true
- true
上面的代码中首先是第一个会话在进行读操作,完成后会结束会话,而第二个操作重新创建了一个新的会话,再次执行了同样的查询,我们发现得到的依然是缓存的结果。
总结:二级缓存是Mapper级别的,也就是说,当一个会话结束时,它的缓存依然会存在于二级缓存中,因此如果我们再次创建一个新的会话会直接使用之前的缓存。当第一个对话没有结束时,它的一级缓存就不会写入二级缓存中,也就是说,此时第二次对话就无法访问到之前的缓存
例子
- public static void main(String[] args) {
- try (SqlSession sqlSession = MybatisUtil.getSqlSession(true)) {
- SqlSession sqlSession1=MybatisUtil.getSqlSession(true);
- TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
- TestMapper testMapper1 = sqlSession1.getMapper(TestMapper.class);
- Student student1 = testMapper1.getStudent("20200512039");
- Student student = testMapper.getStudent("20200512039");
- System.out.println(student == student1);
- sqlSession1.close();
- }
- }
输出
- 开始构造
- 开始构造
- false
添加了二级缓存之后,会先从二级缓存中查找数据,当二级缓存中没有时,才会从一级缓存中获取,当一级缓存中都还没有数据时,才会请求数据库
读取顺序:二级缓存 => 一级缓存 => 数据库
使用useCache属性来关闭缓存使用flushCache="false"在每次执行后都清空缓存,通过这这个我们还可以控制DML操作完成之后不清空缓存。
- <select id="getStudentBySid" resultType="Student" useCache="false" flushCache="true">
- select * from student where sid = #{sid}
- select>
关闭Mybatis的二级缓存
- <settings>
- <setting name="cacheEnabled" value="false"/>
- settings>