一级缓存
- mybatis的一级缓存是SQLSession级别的缓存,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据,不同的SqlSession之间缓存数据区域(HashMap)是互相不影响的。
- 一级缓存的作用域是SqlSession范围的,当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存)中,第二次查询时会从缓存中获取数据,不再去底层进行数据库查询,从而提高了查询效率。
- ==如果SqlSession执行了DML操作(insert、update、delete),并执行commit()操作,mybatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存数据中存储的是最新的信息,避免出现脏读现象==
- 当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了,Mybatis默认开启一级缓存,不需要进行任何配置。
一级缓存的验证
- 第一种方式:同一个代理类对象,两次执行相同的sql查询语句
- 测试代码:
- 结果:
- 总结:通过观察结果可以看出,在第一次查询id为1的Employee对象时执行了一条select语句,但是第二次获取id为1的Employee对象时并没有执行select语句,因为此时一级缓存也就是SqlSession缓存中已经缓存了id为1的Employee对象,Mybatis直接从缓存中将对象取出来,并没有再次去查询数据库,所以第二次也就没有执行select语句
- 测试代码:
- 第二种方式:进行事务提交后,缓存清空:
- 代码
- 结果:
- 在第一次查询id为1的employee对象时执行了一条select语句,接下来执行了一个delete并commit操作,Mybatis为了保证缓存中存储的是最新消息,会清空SqlSession缓存。当第二次获取id为1的User对象时一级缓存也就是SqlSession缓存中并没有缓存任何对象,所以Mybatis再次执行语句去查询id为1的Exployee对象
- 代码
- 第三中方式:执行一次后,关闭sqlSession
- 代码:
- 结果:
- 在第一次查询id为1的User对象时执行了一条select语句,接下来调用SqlSession的close()方法,该方法会关闭SqlSession缓存,当第二次获取id为1的Employee对象时一级缓存也就是SqlSession缓存是一个新的对象,其中并没有缓存任何对象,所以Mybatis再次执行select语句去查询id为1的Employee对象
- 代码:
二级缓存
- 二级缓存是mapper级别的缓存,使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMapper进行数据存储,相比一级缓存SqlSession,二级缓存的范围更大,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
- 二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的SqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
- ==Mybatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。==
禁用二级缓存
- 在statement中设置userCache=false可以禁用当前的select语句的二级缓存,及每次查询都会发出sql区查询,默认值是true
- 这样设置了的sqlstatement每次查询会不使用二级缓存
- 在statement中设置userCache=false可以禁用当前的select语句的二级缓存,及每次查询都会发出sql区查询,默认值是true
新缓存(清空缓存)
- 执行完事务提交的方法 后需要刷新缓存,设置flushcache=true表示刷新缓存,这样可以避免脏读
二级缓存开启
- 第一步:在mybatis的全局配置文件中
- cacheEnabled的value为true表示在此配置文件下开启二级缓存,该属性默认为false。
- 第二步:需要指定开启二级缓存的namepace的mapper,在mepper映射配置文件中开启
- cache元素用来开启当前mapper的namespace下的二级缓存,该元素的属性设置如下:
- flushInterval:刷新间隔,可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段,默认情况下是不设置的,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
- size:缓存数目,可以被设置为任意正整数,要缓存对象数目和运行环境可用内存资源数目,默认值是1024.
- readOnly:只读属性可以被设置为true或false,只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。这提供了很重要的性能优势,可读写的缓存会返回缓存对象的拷贝(通过序列化),这会慢一些,但是安全,因此默认是false。
- eviction:收回策略,默认为LRU,有如下几种
- LRU:最近最少使用的策略,移除最长时间不被使用的对象。
- FIFO:先进先出策略,按对象进入缓存的顺序来移除它们。
- SOFT:软引用策略,移除基于垃圾回收器状态和软引用规则的对象。
- WEAK:弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
- ==注意:==
- 使用二级缓存时,与查询结果映射的java对象必须实现java.io.Serializable接口的序列化和反序列化操作
- 如果存在父类,其成员都需要实现序列化接口,
- 实现序列化接口是为了对缓存数据进行序列化和反序列化操作,因为二级缓存数据存储介质多种多样,不一定在内存,有可能是硬盘或者远程服务器。
二级缓存测试
- 代码:
- 结果:
- 分析:第一次查询是关闭了一级缓存,第二次查询从一级缓存中没有找到,就会从二级缓存中找,并且找到了,所以只有一条sql语句
二级缓存应用场景
- 对于访问多的请求且用户对查询结果实时性要求不高,此时可以采用mybatis二级缓存技术降低数据库访问量,提高访问的速度,业务场景如:耗时较高的统计分析sql,电话账单查询sql
- 实现方法如下:通过设置刷新的时间间隔,由mybatis每隔一段时间自动清空缓存,根据数据变化的频率设置刷新缓存的间隔flushinterval,比如设置为30分钟,60分钟,24小时等,根据需求而定
局限性
- mybatis对细粒度缓存级别的缓存实现不好,比如;对商品信息进行缓存,由于商品信息查询访问量很大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存,就无法实现当一个商品刷新的时候,只刷新该商品的缓存信息,不刷心其他商品的缓存信息,因为mybatis的二级缓存区域以mapper为单位划分,当一个商品的信息的变化,会将所有的商品的缓存数据全部清空,解决此类问题需要在业务层根据需求对数据有针对性的缓存