一、介绍
1、mybatis缓存:
mybatis包含一个非常强大的查询缓存特性,可以非常方便的定制和配置缓存,通过缓存减少Java Application与数据库的交互次数,从而提升程序的运行效率。
2、mybatis一二级缓存
mybatis的缓存分为一级缓存和二级缓存。
(1)默认情况下一级缓存开启(sqlSession级别的缓存,也称本地缓存)
(2)二级缓存需要手动开启,映射器级别的缓存,针对不同的namespace的映射器。为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存
3、工作流程
一级缓存是先去一级缓存区中查找数据,如果没有再去数据库中查找,找到数据后将数据存入一级缓存区;二级缓存是先去二级缓存区域找数据,没有的话再接着去一级缓存区找数据,如果再没有就去数据库里拿数据,拿到后将数据存入二级缓存区。
4、缓存的适应性
- 经常查询并且不经常改变的
- 数据的正确性对最终结果影响不大的
二、一级缓存
1、配置
一级缓存是SqlSession级别。一级缓存的作用域是 SqlSession , Mabits 默认开启一级缓存。 在同一个SqlSession中,执行相同的SQL查询时;第一次会去查询数据库,并写在缓存中,第二次会直接从缓存中取。 当执行SQL时候两次查询中间发生了增删改的操作,则SqlSession的缓存会被清空。一级缓存有两个级别可以设置:session级别和statement级别。默认是session级别。
(1) session级别
即在一个Mybatis会话中执行的所有语句都会共享这一个缓存。
<setting name="localCacheScope" value="session">
(2)statement级别
可以理解为缓存只对当前执行的这一个statement有效。
<setting name="localCacheScope" value="statement">
2、存储原理
一级缓存 Mybatis的内部使用一个HashMap,key为hashcode+statementId+sql语句。Value为查询出来的结果集映射成的java对象。 Sqlsession执行insert、update、delete等操作commit后会清空该SqlSession缓存。
3、工作流程
(1)对于某个Select Statement,根据该Statement生成key;
(2)判断在本地缓存(local cache)中,该key是否有对应的数据存在
(3)如果命中,则跳过查询数据库,拿到本地的数据,继续往下走。
(4)如果没命中,去数据库中查询数据,得到想要的结果
(5)将key和查询到的结果作为一个键值对存入本地缓存(local cache)中
(6)将查询结果返回
(7)判断缓存级别是否为Statement级别,如果是的话,清空本地缓存
4、demo
5、一级缓存失效场景
(1)不同sqlSession对应不同的一级缓存
如果场景了两个不同的sqlSession去查询同样的数据,缓存也会失效;
(2)同一个sqlSession的查询条件不同
(3)同一个sqlSession两次查询期间对数据进行了改变,(即增、删、改)
(4)同一个sqlSession两次查询期间手动清空了缓存
在两次查询之间调用了sqlSession的clearCache()方法清空了缓存,缓存自然也就失效了,第二次就要再去数据库里做查询取数据
三、二级缓存
二级缓存是Mapper级别的缓存,多个SqlSession去操作同一个Mapper中的sql语句,则这些SqlSession可以共享二级缓存,即二级缓存是跨SqlSession的。简单说就是同一个namespace 下的 mapper 映射文件中,用相同的sql去查询数据,会去对应的二级缓存内取结果。
下面看下开启mybatis二级缓存的几种方法:
1、mybatis开启本地二级缓存
以springboot项目为例
1.1、项目配置文件
# 开启二级缓存mybatis-plus.configuration.cache-enabled=true
1.2、mapper.xml
在需要开启的mapper.xml中,添加以下代码(在<mapper namespace>下方)
<!--开启二级缓存-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
eviction可用的清除策略有:LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
1.3、mapper参数配置
(1)查询参数:
当为select查询语句时:
flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存;
useCache默认为true,表示会将本条语句的结果进行二级缓存。
设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。所以:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap" useCache="true">select <include refid="Base_Column_List" />from t_dm_custwhere id = #{id,jdbcType=BIGINT}</select>
(2)更新操作
当为insert、update、delete语句时:
flushCache默认为true,表示任何时候语句被调用,都会导致本地缓存和二级缓存被清空;
useCache属性在该情况下没有。
一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。但是如果你不想刷新缓存只需要这么做:
<update id="updateByPrimaryKey" parameterType="com.sl.domain.TDmCust" flushCache=false>update t_dm_custset create_time = #{createTime,jdbcType=TIMESTAMP}where id = #{id,jdbcType=BIGINT}</update>
最后,测试验证:
打开谷歌浏览器发送查询请求时可以看到控制台有sql打印出来。再用IE浏览器发送查询请求发现第二次未打出sql,说明第二次sql查询已经走了mybatis二级缓存。
2、使用redis开启二级缓存
2.1、pom:
加上redis maven配置
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2、配置文件
spring.redis.host=localhost
spring.redis.port=6379
因为这个类不是Spring管理的,所以通过MyBatisRedisSpringContext从ioc容器里获取redisTemplate类
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;@Component
public class MyBatisRedisSpringContext implements ApplicationContextAware, DisposableBean {private static ApplicationContext applicationContext = null;@Overridepublic void destroy() throws Exception {}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {MyBatisRedisSpringContext.applicationContext = applicationContext;}public static ApplicationContext getApplicationContext() {return applicationContext;}public static <T> T getBean(String name) {return (T)applicationContext.getBean(name);}public static <T> T getBean(Class clz) {return (T)applicationContext.getBean(clz);}}
2.3、mapper文件配置
<cache type="com.demo.cache.MybatisRedisCache" />