MyBatis——查询缓存

一、MyBatis的缓存介绍
 和大多数持久层框架一样,MyBatis同样提供了一级缓存和二级缓存的支持。一级缓存是基于 PerpetualCache 的 HashMap 的本地缓存,其缓存的作用域为 Session,当 Session flush 或 close 之后,该Session中的所有缓存就会被清空。二级缓存与一级缓存机制相同,默认也是采用PerpetualCache的HashMap进行存储,不同之处在于其存储作用域为 Mapper(即同一个Namespace),并且可自定义存储源,如 Ehcache、Hazelcast等。
 默认情况下,MyBatis是启用一级缓存的,它是SqlSession级别的,也即同一个SqlSession对象调用相同的Select语句,就会从一级缓存中直接拿到结果,而不是再去查询一次数据库。默认情况下,Select会使用缓存,增删改不使用。当 Session flush 或 close 之后,该Session中的所有 Cache 都将清空。
 MyBatis的缓存机制及二级缓存的工作模式:
在这里插入图片描述
二、一级缓存
 MyBatis默认是开启一级缓存的,即在同一个SqlSession中,使用同样的查询条件调用同一个查询方法(调不同的方法即使SQL文完全相同也不会使用一级缓存,当然一般而言同一个接口中不会定义逻辑完全相同的多个方法)时只有第一次会发送SQL到数据库进行查询,后续都会直接从一级缓存中获取缓存结果,但一级缓存在以下情况下会失效:
  1️⃣更改查询条件(这个不算失效,而是本身就没有缓存)
  2️⃣方法中查询使用的SqlSession对象不是同一个,比如在方法中执行了sqlSession.close()之后又新开一个SqlSession
  3️⃣是同一个SqlSession对象,但执行过sqlSession.clearCache();清空了缓存
  4️⃣增删改操作之后,一旦方法中有增删改的操作,SqlSession就会清空缓存

 一级缓存的原理:MyBatis内部缓存使用了一个HashMap,HashMap是键值对的结构,键值对中的key是hashCode + 查询的SqlId(即XxxMapper.xml中的id) + 编写的sql查询语句,键值对的值是执行查询后所获得Java对象。一级缓存的作用域是SqlSession,每次查询先找缓存,找到了就使用,找不到再从数据库查询,查询到数据后会将数据写入一级缓存。

三、二级缓存
 一级缓存只在同一个SqlSession中有效,但有时我们可能会在多个方法中调用同一个查询方法且参数也有可能相同,在多个方法中必然不能使用同一个SqlSession,这时仅仅使用一级缓存就获取不到数据了,因为一级缓存中缓存的数据在SqlSession提交之后会被清空,这时就需要开启二级缓存。MyBatis中的二级缓存是Mapper级别的,多个SqlSession去操作同一个Mapper的接口方法(SQL)时,这多个SqlSession之间的二级缓存是共用的,即二级缓存是跨SqlSession的。
 1、开启二级缓存的步骤:
  ①在mybatis-config.xml中配置开启二级缓存

<settings>
	<!-- 全局映射器启用缓存 -->
	<setting name="cacheEnabled" value="true" />
</settings>

在这里插入图片描述
  注意:用二级缓存时Entity类必须实现序列化接口

@Data
public class Student implements Serializable {
	private static final long serialVersionUID = -2777225822411336118L;
	private String id;
	private String name;
	private Integer age;
	private String email;
}

  ②在XxxMapper.xml中添加一个<cache>:

<mapper namespace="com.bdm.mappers.StudentMapper">
	<!-- 缓存的具体方案 -->
	<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />
	<update id="update" parameterType="com.bdm.entities.Student">
		UPDATE students SET
		name=#{name},age=#{age},email=#{email}
		WHERE
		id=#{id}
	</update>
	<select id="getById" parameterType="string" resultType="com.bdm.entities.Student">
		SELECT *
		FROM students WHERE id = #{id}
	</select>
</mapper>

  测试:

public class MainTest {

	private static final String SOURCE = "mybatis-config.xml";
	private static SqlSessionFactory sqlSessionFactory = null;
	private SqlSession sqlSession = null;

	static {
		InputStream inputStream;
		try {
			inputStream = Resources.getResourceAsStream(SOURCE);
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Before
	public void before() {
		sqlSession = sqlSessionFactory.openSession();
	}

	@Test
	public void test() {
		StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
		Student s1 = mapper.getById("1");
		Student s2 = mapper.getById("1");
		sqlSession.close();
		sqlSession = sqlSessionFactory.openSession();
		StudentMapper mapper1 = sqlSession.getMapper(StudentMapper.class);
		Student s3 = mapper1.getById("1");
		Student s4 = mapper1.getById("1");
		System.out.println(s1.toString());
		System.out.println(s2.toString());
		System.out.println(s3.toString());
		System.out.println(s4.toString());
	}

	@After
	public void after() {
		try {
			sqlSession.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			sqlSession.close();
		}
	}
}

   后台只打印一条SQL语句:说明二级缓存启用了

 2、二级缓存的参数说明

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

  关于配置的参数说明如下:
   1️⃣eviction:缓存的回收策略,此处使用的是FIFO(先进先出)的策略,即进入缓存时间最久的对象会先被清理
   2️⃣flushInterval:缓存刷新的频率,每60000毫秒(即1分钟)刷新一次缓存
   3️⃣size:缓存的大小,注意此处是指缓存的对象的个数(默认是1024),而非空间,当缓存的对象达到512个时就会执行eviction中指定的回收策略将对象从缓存中移除
   4️⃣readOnly:缓存中的对象是否是只读的,属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化),这会慢一些,但是安全,因此默认是false
  缓存的回收策略有以下四种可选:
   1️⃣LRU:最近最少使用,移除最长时间不被使用的对象,它是默认
   2️⃣FIFO:先进先出,按对象进入缓存的顺序来移除它们
   3️⃣SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
   4️⃣WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象
 3、禁用二级缓存

<select id="getUserById" parameterType="Integer" resultType="User" useCache="false">
	select * from tbl_user where id=#{id}
</select>

  设置useCache值为false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存
 4、刷新(清空)二级缓存

<update id="updateUser" parameterType="User" flushCache="true">
	UPDATE tbl_user SET NAME=#{name},age=#{age} WHERE id=#{id}
</update>

  insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读,所以默认为true,默认情况下为true即刷新缓存,一般不用修改。

四、一级缓存和二级缓存的使用顺序
 如果你的 MyBatis 使用了二级缓存,并且你的 Mapper 和 select 语句也配置使用了二级缓存,那么在执行 select 查询的时候,MyBatis 会先从二级缓存中取,其次才是一级缓存,即 MyBatis 查询数据的顺序是:

查不到
查不到
二级缓存
一级缓存
数据库

五、MyBatis中使用第三方二级缓存框架
 EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider(MyBatis默认的二级缓存是PerpetualCache)。
 整合步骤:
  ①引入EhCache的依赖或导入相关jar:
   mybatis-ehcache-1.0.2.jar
   ehcache-core-2.7.1.jar
  ②在src根目录下创建EhCache的配置文件:ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<diskStore path="D:\ehcache" />
	<defaultCache maxElementsInMemory="1000"
		maxElementsOnDisk="10000000" 
		eternal="false" 
		overflowToDisk="false"
		timeToIdleSeconds="120" 
		timeToLiveSeconds="120"
		diskExpiryThreadIntervalSeconds="120" 
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>

   参数说明:
    <diskStore/>:指定数据在磁盘中的存储位置
    <defaultCache>:当借助CacheManager.add(“demoCache”)创建Cache时,EhCache便会采用<defalutCache/>指定的管理策略
   以下属性是必须的:
    a、maxElementsInMemory:在内存中缓存的element的最大数目
    b、maxElementsOnDisk:在磁盘上缓存的element的最大数目,若是0表示无穷大
    c、eternal:设定缓存的elements是否永不过期,如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
    d、overflowToDisk:设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
   以下属性是可选的:
    a、timeToIdleSeconds:当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
    b、timeToLiveSeconds:缓存element的有效生命期,默认是0,也就是element存活时间无穷大
    c、diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小,默认是30MB,每个Cache都应该有自己的一个缓冲区
    d、diskPersistent:在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false
    e、diskExpiryThreadIntervalSeconds:磁盘缓存的清理线程运行间隔,默认是120秒,每隔120s,相应的线程会进行一次EhCache中数据的清理工作
    f、memoryStoreEvictionPolicy:当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
  ③开启ehcache缓存:修改XxxMapper.xml文件,在<cache>中指定使用EhcacheCache

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bdm.mappers.StudentMapper">
	<!-- 使用EhCache作为二级缓存管理器 -->
	<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
	<update id="update" parameterType="com.bdm.entities.Student">
		UPDATE students SET
		name=#{name},age=#{age},email=#{email}
		WHERE
		id=#{id}
	</update>
	<select id="getById" parameterType="string" resultType="com.bdm.entities.Student">
		SELECT *
		FROM students WHERE id = #{id}
	</select>
</mapper>
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值