Mybatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
Mybatis 系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存(
SqlSession
级别的缓存、也称作本地缓存) - 二级缓存需要手动开启和配置,它是基于
namespace
级别的缓存
一级缓存(本地缓存)
一级缓存作用域默认为SqlSession
。同一次会话期间只要查询过的数据都会保存在当前SqlSession
的一个Map
中。当 Session
flush 或 close 后, 该Session
中的所有 Cache 将被清空。
一级缓存不能被关闭, 但可以调用 clearCache()
来清空本地缓存, 或者改变缓存的作用域。
(1)修改缓存作用域
可以在全局配置文件修改一级缓存的缓存作用域
mybatis-config.xml
<settings>
<setting name="localCacheScope" value="STATEMENT"/>
</settiongs>
(2)失效的情况
- 不同的
SqlSession
对应不同的一级缓存
如下面的变量 sqlSession 和 sqlSession 1
@Test
public void test() {
SqlSession sqlSession = MyTest.getSqlSessionFactory().openSession();
SqlSession sqlSession1 = MyTest.getSqlSessionFactory().openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class);
System.out.println(mapper.getEmployeeByDid(2));
sqlSession.close();
System.out.println(mapper1.getEmployeeByDid(2));
sqlSession1.close();
}
- 同一个
SqlSession
但是查询条件不同
如下面调用getEmployeeByDid
的输入参数不同
@Test
public void test() {
SqlSession sqlSession = MyTest.getSqlSessionFactory().openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
System.out.println(mapper.getEmployeeByDid(1));
System.out.println(mapper.getEmployeeByDid(2));
sqlSession.close();
}
- 同一个
SqlSession
两次查询期间执行了任何一次增删改操作
如下面在两次查询方法间执行了删除方法deleteEmployee
@Test
public void test() {
SqlSession sqlSession = MyTest.getSqlSessionFactory().openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
System.out.println(mapper.getEmployeeByDid(1));
mapper.deleteEmployee(3);
System.out.println(mapper.getEmployeeByDid(2));
sqlSession.close();
}
- 同一个
SqlSession
两次查询期间手动清空了缓存
如下面调用了clearCache()
方法清空缓存
@Test
public void test() {
SqlSession sqlSession = MyTest.getSqlSessionFactory().openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
System.out.println(mapper.getEmployeeByDid(1));
sqlSession.clearCache();
System.out.println(mapper.getEmployeeByDid(2));
sqlSession.close();
}
二级缓存
二级缓存全局作用域缓存。
- 二级缓存默认不开启,需要手动配置
MyBatis
提供二级缓存的接口以及实现,缓存实现要求 POJO 实现Serializable
接口- 二级缓存在
SqlSession
关闭或提交之后才会生效
(1)步骤
- 全局配置文件中开启二级缓存
<settiengs>
<setting name="cacheEnabled" value="true"/>
</settings>
- 需要使用二级缓存的映射文件处使用
cache
配置缓存
<cache/>
- POJO 需要实现
Serializable
接口
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String lastName;
private String email;
private String gender;
}
(2)cache相关属性
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
eviction
:缓存回收策略:LRU
– 最近最少使用的:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:移除基于垃圾回收器状态和软引用规则的对象。WEAK
– 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。- 默认的是
LRU
。
flushInterval
:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新size
:引用数目,正整数,默认值是 1024。
代表缓存最多可以存储多少个对象,太大容易导致内存溢出readOnly
:只读,属性可以被设置为 true 或 false,默认是 false。- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象
不能被修改。这提供了很重要的性能优势。 - false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,
但是安全。
- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象
缓存相关设置
除了上面提到的setting
的子元素cacheEnable
和sqlSession
的方法flushCache()
,还有:
select
标签的useCache
属性:
配置这个select
是否使用二级缓存。一级缓存一直是使用的<select id="getEmployee" resultType="employee" useCache="true"> select * from employee where id = #{id} </select>
- sql 标签的
flushCache
属性:
增删改默认flushCache=true
。sql 执行以后,会同时清空一级和二级缓存。
查询默认flushCache=false
<select id="getEmployee" resultType="employee" flushCache="true"> select * from employee where id = #{id} </select>
第三方缓存整合
E h C a c h e EhCache EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 H i b e r n a t e Hibernate Hibernate 中默认的
CacheProvider
。
步骤
- 导入echcahe需要的包,以及整合包、日志包。
pom.xml
<dependencies>
<!-- java的线程内缓存框架 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha5</version>
</dependency>
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
- 编写 ehcache.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="E:\ehcache" />
<defaultCache
maxElementsInMemory="10000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!--
属性说明:
l diskStore:指定数据在磁盘中的存储位置。
l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
l maxElementsInMemory - 在内存中缓存的element的最大数目
l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
-->
- 配置 cache 标签
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
参考缓存:若想在命名空间中共享相同的缓存配置和实例。可以使用 cache-ref 元素来引用另外一个缓存。
<!-- 引用缓存:namespace:指定和哪个名称空间下的缓存一样 -->
<cache-ref namespace="com.atguigu.mybatis.dao.EmployeeMapper"/>