一级缓存
@Autowired
private SqlSessionFactory sqlSessionFactory;
public void test() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//分页参数
RowBounds rowBounds1 = new RowBounds(0, 5);
RowBounds rowBounds2 = new RowBounds(0, 5);
List<User> userList1 = sqlSession.selectList("com.chy.mall.dao.UserMapper.findUserByName", "张三", rowBounds1);
//命中一级缓存,直接返回一级缓存中的对应的结果对象,不查询数据库
List<User> userList2 = sqlSession.selectList("com.chy.mall.dao.UserMapper.findUserByName", "张三", rowBounds2);
//true
System.out.println(userList1 == userList2);
sqlSession.close();
}
一级缓存的生命周期在sqlSession的生命周期之内,即在mapper方法调用的生命周期之内。
//一个sqlSession,open-> close,方法调用结束时一级缓存会被清空
userMapper.findUserByName();
//一个新的sqlSession
userMapper.findUserByName();
一级缓存默认是开启的,生命周期很短,一般不存在数据读写问题,可以放心用,但基本没啥用。
一级缓存的命中条件
- 方法id相同:调用的要是同一个mapper中的同一个方法
- 查询参数相同:传给sql语句的参数值要全部相同
select * from tb_user where id=?
不管是直接传入int型的id、Integer型的id,还是通过实体类User、Map传入的id,只要最终传给sql语句的值完全相同就行。是相同,10、10.0相等但不相同。
- 分页参数要相同。传递的值相同即可,不要求是同一个对象。
实质是保证执行的sql语句完全相同。
一级缓存的生命周期
spring整合mybatis
- 未使用事务时:每个mapper方法调用都是使用打开一个新的sqlSession,方法调用结束sqlSession被关闭。不管mapper方法调用结果如何,方法调用结束时一级缓存即被清空。
- 使用事务时:会把这个事务中的所有mapper方法调用放在一个事务中,这些mapper方法使用同一个sqlSession,spring将sqlSession存储在ThreadLocal中,在此事务的生命周期中一直有效。不管事务执行结果如何(提交成功、回滚),只要事务结束一级缓存即被清空。
sqlSession调用selectXxx()方法得到结果集时写入缓存。
一级缓存被清除的几种情况
- 调用sqlSession.clearCache()清除缓存
- 调用sqlSession.close()关闭sqlsession
- 调用sqlSession.commit()或sqlSession.rollback(),事务结束
二级缓存
一级缓存是在一个sqlSession的生命周期内,二级缓存是在一个sqlSessionFactory的生命周期内,可以跨sqlSession。
二级缓存的工作流程
二级缓存的命中条件
和一级缓存的命中条件相同,区别是二级缓存命中是在同一个sqlSessionFactory的生命周期内。
二级缓存的生命周期
- 写入时机:sqlSession.selectXxx()查询后执行sqlSession.close()或手动调用sqlSession.commit()时。
- 清空时机:调用mapper的增删改方法更新数据时(实质都是调用sqlSession.update()方法)
二级缓存的使用
- application.properties中启用二级缓存:mybatis.configuration.cache-enabled=true
- xml映射文件中开启二级缓存:<mapper>下加一个子标签:<cache />
- 实体类 implements Serializable 允许序列化
二级缓存的清除策略
一级缓存、二级缓存都是使用map存储数据,一级缓存存在时间短,没有设置map的容量限制;二级缓存存在时间长,可在<cache />中指定map的最大容量、二级缓存清除策略,当二级缓存达到容量限制时会自动清除部分数据。
提供的清除策略如下
二级缓存默认关闭,因为缓存存在时间长,容易产生脏读问题,实际基本不用,也不建议使用。
一级缓存、二级缓存的缓存粒度都是整个结果集,而非结果集中的单条记录,是以结果集为单位进行缓存。