Mybatis缓存
Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存,缓存何以极大的提升查询效率
Mybatis系统中默认定义了两个缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启,(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,它是基于namespace级别的缓存,也就是一个Mapper
为了提高扩展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存
一级缓存
@Test
public void testGetOneUser(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println("----------------");
User user1 = mapper.queryUserById(1);
System.out.println(user==user1);
sqlSession.close();
}
由上图可以看到,对同一个结果查询了两次,但真正执行的查询只有一次,第二次存到了缓存中,且得到的两个结果是相同的。
缓存失效的情况
1.两次查询的结果不一样
@Test
public void testGetOneUser(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println("----------------");
User user1 = mapper.queryUserById(2);
System.out.println(user==user1);
sqlSession.close();
}
2.有增删改操作,增删改操作可能会改变原来的数据所以必定会刷新缓存
public void testUpdate(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
User userx = new User();
userx.setId(2);
userx.setName("HHHH");
userx.setPwd("xxxxxxx");
int i = mapper.updateUser(userx);
System.out.println(i);
System.out.println("----------------");
User user1 = mapper.queryUserById(1);
System.out.println(user==user1);
sqlSession.close();
}
3. 查询不同的mapper,
比如说一个是StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
另一个是UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
这两个mapper是由一个sqlSession得出的,那么缓存也就会失效
-
手动清理缓存
@Test public void testGetOneUser(){ SqlSession sqlSession = MyBatisUtil.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println("----------------"); sqlSession.clearCache(); ///清理缓存 User user1 = mapper.queryUserById(1); System.out.println(user==user1); sqlSession.close(); }
总结
一级缓存默认是开启的,只在一次sqlSession中有效。也就是拿到连接和关闭连接这个区间内,一级缓存就是一个map
二级缓存
-
二级缓存也叫做全局缓存,一级缓存的作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间对应一个二级缓存
-
工作机制
-
- 一个会话查询一条数据,这个数据会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没有了,但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
步骤
-
开启全局缓存
<setting name="cacheEnable" value="true"/>
-
在mapper.xml中配置
<!--开启二级缓存--> <!-- eviction:缓存策略, flushInterval刷新间隔,size:缓存大小,readOnly是否只读 --> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"></cache>
-
一次测试
@Test public void testSecondaryCache(){ SqlSession sqlSession1 = MyBatisUtil.getSqlSession(); SqlSession sqlSession2 = MyBatisUtil.getSqlSession(); UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); User user1 = mapper1.queryUserById(1); System.out.println(user1); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user1 == user2); sqlSession1.close(); sqlSession2.close(); }
并没有使用缓存,这是为什么呢?
是因为在一次查询中,一级缓存死掉,也就是一次sqlSession关闭后,才会将内容缓存到二级缓存中。所以代码应该这样。
@Test
public void testSecondaryCache(){
SqlSession sqlSession1 = MyBatisUtil.getSqlSession();
SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user1 = mapper1.queryUserById(1);
System.out.println(user1);
sqlSession1.close(); //关掉一级缓存
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user1 == user2);
sqlSession2.close();
}
总结
只要开启二级缓存,在同一个Mapper下就有效
所有数据都会先放在一级缓存中。
只有当会话提交或者关闭的时候才会提交到二级缓存中