一级缓存
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>