08 Mybatis 缓存
Mybatis 作为持久层框架也提供了缓存策略,提供缓存策略来减少数据库的查询,提高性能。

一、Mybati 一级缓存
一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。默认是开启的
测试根据id查询用户
@Test
public void findById(){
User user1 = userMapper.findById(41);
System.out.println("第一次查:"+user1);
User user2 = userMapper.findById(41);
System.out.println("第二次查:"+user1);
System.out.println(user1 == user2);
}
结果
第一次查:User(id=41, username=老王, birthday=Tue Feb 27 17:47:08 CST 2018, sex=男, address=北京, accounts=null)
第二次查:User(id=41, username=老王, birthday=Tue Feb 27 17:47:08 CST 2018, sex=男, address=北京, accounts=null)
true
sql记录

因此,两个查询的对象完全相同,而且第二次的查询没有发出sql语句。
那么一级缓存会存在读取到可能已经修改的数据怎么办?(脏读)
一级缓存的分析
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改
,添加
,删除
,commit()
,close()
等方法时,就会清空一级缓存。

第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。
测试清空一级缓存 使用commit
@Test
public void findById(){
User user1 = userMapper.findById(41);
System.out.println("第一次查:"+user1);
session.commit();
User user2 = userMapper.findById(41);
System.out.println("第二次查:"+user1);
System.out.println(user1 == user2);
}
结果

发出了两次的查询,而且两次查询的返回对象都是不同的。即清空了一级缓存,而且发出了两次的sql查询。
一、Mybati 二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

第1步:开启二级缓存
在 SqlMapConfig.xml 文件开启二级缓存
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
第2步:配置相关的 Mapper 映射文件
<mapper namespace="com.it.Mapper.UserMapper">
<!-- 开启二级缓存的支持 -->
<cache></cache>
</mapper>
<cache>
标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
第3步:置 配置 statement 上面的 useCache
<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
将 UserDao.xml 映射文件中的<select>
标签中设置 useCache="true"
代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。
注意
:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。避免脏读。
二级缓存测试:
那么二级缓存的测试就需要用不同的SqlSession来操作了。
@Before//在测试方法执行之前执行
public void init()throws Exception {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.创建 SqlSession 工厂对象
factory = builder.build(in);
//4.创建 SqlSession 对象
session1 = factory.openSession();
session2 = factory.openSession();
//5.创建 Dao 的代理对象
userMapper1 = session1.getMapper(UserMapper.class);
userMapper2 = session2.getMapper(UserMapper.class);
}
测试一
@Test
public void findById(){
User user1 = userMapper1.findById(41);
System.out.println("第一次查:"+user1);
User user2 = userMapper2.findById(41);
System.out.println("第二次查:"+user2);
System.out.println(user1 == user2);
}
这里session1的mapper查询完,立马使用session2去查询

可以看到发出了两次的sql查询。
测试二
先用一个session去查,查完了关闭session1(即关闭了session1一级缓存),然后再用session2去查询
@Test
public void findById(){
User user1 = userMapper1.findById(41);
System.out.println("第一次查:"+user1);
session1.close(); //将一级缓存关闭
User user2 = userMapper2.findById(41);
System.out.println("第二次查:"+user2);
System.out.println(user1 == user2);
}

只发出了一次sql查询语句,用的是session1的二级缓存
但是查询到的对象是不同的,也就是说,session2用session1中的二级缓存结果重新开辟的一个新的对象,内容相同。
二级缓存注意事项
- 当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。