MyBatis缓存
几乎所有的ORM框架都提供了缓存机制。
MyBatis框架包含了一个非常强大的缓存特征,它可以很方便的配置使用。实际开发中,缓存可以极大的提升查询的效率
MyBatis中的有两个缓存,一级缓存和二级缓存。默认情况下,只有一级缓存是开启的,一级缓存是SqlSession级别的缓存,二级缓存需要手动开启和配置,二级缓存是namespace级别的缓存
MyBatis中也提供了缓存接口,我们也可以整合第三方的二级缓存插件来使用,提高扩展性
一级缓存
一级缓存是SqlSession级别的缓存,共享范围就在一个SqlSession内。一级缓存默认就是存在的,我们只需要证明一下它的存在就可以了
@Test
public void test4(){
IUserMapper iUserMapper = sqlSession.getMapper(IUserMapper.class);
User user = iUserMapper.findById(1);
System.out.println(user);
// sqlSession.clearCache();//手动清空缓存,清空和不清空的情况下运行一边就能看出不同
User user2 = iUserMapper.findById(1);
System.out.println(user2);
}
1.首先使用同一个SqlSession执行了两个根据id查询,并且条件相同,这时就能发现MyBatis执行了一次查询数据库的操作:
2.在执行第一个查询后,手动清空一下SqlSession中的缓存,虽然条件相同,但是还是会查询两次数据库
注意:
一级缓存是SqlSession级别的缓存,当SqlSession进行了增、删、改、commit、close操作时,就会清空一级缓存
二级缓存
二级缓存是同一个命名空间(namespace)级别的缓存,它们有一个共同cache。也就是二级缓存可以被多个SqlSession共享。
在mybatis-config.xml文件中开启二级缓存
<settings>
<!-- 开启二级缓存,默认就是开启,可以忽略-->
<setting name="cacheEnabled" value="true"/>
</settings>
在mapper映射文件中加入一个cache标签:
<!-- 表示这个mapper中查询的结果都会保存到二级缓存-->
<cache/>
没有特殊需求时,写一个< cache/>标签即可,如果有需要可以根据其中的属性进行配置
cache标签中的属性:
- eviction:缓存回收策略:
- LRU:移除最长时间不用的对象
- FIFO:根据对象进入缓存的顺序来移除对象,先进先出
- SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK:弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象
- flushinterval:缓存的刷新间隔,毫秒值。默认为不刷新
- readOnly:是否为只读,true为只读,false读写(默认)
- size:存放元素的个数
- type:指定自定义缓存的全类名
- blocking:设置缓存中找不到对应key是否一直blocking,直到有对应数据进入缓存
在mapper映射文件中准备一个开启二级缓存的sql:
<!-- useCache为true,表示这个Statement会使用二级缓存-->
<select id="findById" parameterType="int" resultType="User" useCache="true">
select * from t_user where id=#{id}
</select>
POJO类:
使用MyBatis中的二级缓存时,所缓存的对象一定要实现序列化接口Serializable
public class User implements Serializable {
private static final long serialVersionUID = 3800957211972451743L;
private Integer id;
private String username;
private String password;
private String email;
private String remark;
}
测试:
创建两个不同的SqlSession对象,都去查询同一个数据,findById(1)
@Test
public void test4(){
SqlSession sqlSession1 = factory.openSession();
IUserMapper iUserMapper = sqlSession1.getMapper(IUserMapper.class);
User user1 = iUserMapper.findById(1);
System.out.println("--------------------------"+user1);
sqlSession1.commit();
sqlSession1.close();
SqlSession sqlSession2 = factory.openSession();
IUserMapper iUserMapper2 = sqlSession2.getMapper(IUserMapper.class);
User user2 = iUserMapper2.findById(1);
System.out.println("--------------------------"+user2);
}
查看结果:
补充:
- 二级缓存是以namespace为单位的,不同的namespace中的操作互不影响
- 多表查询的时候不要使用二级缓存,因为在不同的namespace中更新了其中一个表中的数据,一定会发生脏读,使用cache-ref来进行namespace的依赖也可以避免这种情况的发生
- 二级缓存是表级缓存,开销大,一级缓存是使用HashMap存储,效率更高
- 二级缓存在实际开发中其实并不常用,我们可以使用一些缓存插件,或者将缓存工作交给其他框架处理