目录
1.5SqlSession相同,但是两次查询期间执行了增删改操作
前言
- mybatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制,缓存可以极大的提升查询效率。
- mybatis系统中默认定义了两级缓存。
一级缓存和二级缓存
➢默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
➢二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
➢为了提高扩展性,Mybatis定义了缓存接口Cache,可以通过实现Cache接口来自定义二级缓存。
1.一级缓存(本地缓存)
1.1缓存机制
a.一级缓存是sqlSession级别的缓存,不同的sqlSession不能共享一级缓存,
b.一级缓存是默认开启的
c.一级缓存的底层实现就是一个SqlSession级别的一个Map.key:hashCode+查询的SqlId+编写的sql查询语句+参数
1.2一级缓存的工作机制
与数据库的同一次会话期间查询到的数据会被放在当前的一级缓存中,以后如果需要获取相同的数据,直接从缓存中获取,不会再从数据库中获取,如果从缓存中获取不到,则还需要到数据库获取。
1.3同一个SqlSession(一级缓存的体现)
mapper接口
<select id="getEmployeeById" resultType="employee" useCache="true">
select * from
tbl_employee where id=#{id}
</select>
test测试
@Test
public void testFirstLevleCache() throws IOException {
SqlSessionFactory ssc = getSqlSessionFactory();
SqlSession session = ssc.openSession();
try {
EmployeeMapperCache mapper = session.getMapper(EmployeeMapperCache.class);
// 与数据库的同一次会话期间查询到的数据会被放在当前会话的一级缓存中
Employee employee1 = mapper.getEmployeeById(2);
System.out.println(employee1);
System.out.println("----第二次查询----");
Employee employee2 = mapper.getEmployeeById(2);
System.out.println(employee2);
} catch (Exception e) {
// TODO: handle exception
} finally {
session.close();
}
}
测试结果
日志 和输出中:
第一次查询发送了SQL语句,后返回了结果
第二次查询没有发送SQL语句,直接从内存中获取了结果,这是一级缓存的体现
1.4不同的SqlSession
test测试
@Test
public void testFirstLevleCache() throws IOException{
SqlSessionFactory ssc = getSqlSessionFactory();;
SqlSession session = ssc.openSession();
SqlSession session1 = ssc.openSession();
try {
EmployeeMapperCache mapper = session.getMapper(EmployeeMapperCache.class);
//与数据库的同一次会话期间查询到的数据会被放在当前会话的一级缓存中
Employee employee1 = mapper.getEmployeeById(2);
System.out.println(employee1);
System.out.println("----------");
EmployeeMapperCache mapper1 = session1.getMapper(EmployeeMapperCache.class);
Employee employee2 = mapper1.getEmployeeById(2);
System.out.println(employee2);
} catch (Exception e) {
// TODO: handle exception
}finally{
session.close();
}
}
其他配置同上1.3
测试结果
在代码中分别使用 SqlSesion 和 SqlSession1 进行了相同的查询,第一次查询发送了sql语句,第二次查询发送了sql语句,说明一级缓存失效了
1.5SqlSession相同,但是两次查询期间执行了增删改操作
mapper映射
<!-- 增删改之后清空一二级缓存 -->
<delete id="deleteEmployeeById" flushCache="true">
delete from tbl_employee where id=#{id}
</delete>
test测试
//SqlSession相同,但是两次查询期间执行了增删改操作,(这次的增删改操作会影响当前缓存中的数据,因为可能数据库的数据发生了变化
@Test
public void testFirstLevleCache1() throws IOException{
SqlSessionFactory ssc = getSqlSessionFactory();;
SqlSession session = ssc.openSession();
try {
EmployeeMapperCache mapper = session.getMapper(EmployeeMapperCache.class);
//与数据库的同一次会话期间查询到的数据会被放在当前会话的一级缓存中
Employee employee1 = mapper.getEmployeeById(7);
System.out.println(employee1);
System.out.println("----------");
//两次会话之间执行了增删改操作
mapper.deleteEmployeeById(7);
session.close();
Employee employee2 = mapper.getEmployeeById(7);
System.out.println(employee2);
} catch (Exception e) {
// TODO: handle exception
}finally{
session.close();
}
}
测试结果
代码中使用相同的SqlSession,但是两次查询期间执行了增删改操作,第一次查询发送了sql语句,第二次查询发送了sql语句,说明一级缓存失效了
1.6SqlSession相同,手动清除了一次缓存
test测试
//SqlSession相同,手动清除一次缓存
@Test
public void testFirstLevleCache2() throws IOException{
SqlSessionFactory ssc = getSqlSessionFactory();;
SqlSession session = ssc.openSession();
try {
EmployeeMapperCache mapper = session.getMapper(EmployeeMapperCache.class);
//与数据库的同一次会话期间查询到的数据会被放在当前会话的一级缓存中
Employee employee1 = mapper.getEmployeeById(1);
System.out.println(employee1);
System.out.println("----------");
//手动清除一次缓存
session.clearCache();
Employee employee2 = mapper.getEmployeeById(1);
System.out.println(employee2);
} catch (Exception e) {
// TODO: handle exception
}finally{
session.close();
}
}
其他配置同上1.3
测试结果
代码中使用相同的SqlSession,但手动清除了一次缓存,第一次查询发送了sql语句,第二次查询发送了sql语句,说明一级缓存失效了
1.7一级缓存总结
一级缓存失效的几种情况:
(1). SqlSession不同.
(2). SqlSession相同,但是查询条件不同(当前的SqlSession对应的一级缓存中还没有这条数据)
(3). SqlSession相同,但是两次查询期间执行了增删改操作. (这次的增删改操作会影响当前缓存中的数据,因为可能 数据库中的数据会发生变化)
(4). SqlSession相同,手动清除了一次缓存
2.二级缓存
2.1二级缓存
二级缓存(second level cache),全局作用域缓存,是基于namespace级别的缓存,默认不开启,需要手动配置。
mybatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
二级缓存在SqlSession关闭或提交之后才会生效
2.2二级缓存的工作机制
一个SqlSession从数据库查询数据,会把查询到的数据存放到当前SqlSession的一级缓存中。当SqlSession进行提交commit或者关闭close操作时,就会把一级缓存中的数据转移到当前namespace对应的二级缓存中。
当执行一条查询SQL时,流程为 A:从二级缓存中进行查询
B:进入一级缓存中查询
C:执行JDBC查询
2.3二级缓存使用步骤
1、全局配置文件mybatis.xml中开启二级缓存
<settings>
<!-- setting中包含了很多重要的设置项,可以影响mybatis框架的运行行为 -->
<!-- 配置使用二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
2、需要使用二级缓存的SQL映射文件处使用cache配置缓存
<!-- 配置二级缓存
eviction(收回):缓存的回收策略 ,
默认是LRU LRU - 最近最少使用的:移除最长时间不被使用的对象
FIFO- 先进先出:按对象进入缓存的顺序来移除它们
SOFT- 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK- 弱引用:更加积极的移除基于垃圾回收器状态和软引用规则的对象
flushInterval(刷新间隔):缓存的刷新间隔,可以配置多久一次清空缓存,默认是不清空的
readOnly:设置缓存为只读
size:缓存中可以缓存的数据数量
type:自定义缓存或者使用第三方缓存技术时,通过type来指定具体使用的缓存类 -->
<cache eviction="LRU" flushInterval="60000" readOnly="false"
size="1000"></cache>
3、注意: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;
private Department dept;
其他省略
}
4.test测试
//二级缓存 两个session对应同一个namespace,可以使用二级缓存
@Test
public void testSecondLevleCache() throws IOException{
SqlSessionFactory ssc = getSqlSessionFactory();;
SqlSession session = ssc.openSession();
SqlSession session1 = ssc.openSession();
try {
EmployeeMapperCache mapper = session.getMapper(EmployeeMapperCache.class);
EmployeeMapperCache mapper1 = session1.getMapper(EmployeeMapperCache.class);
//与数据库的同一次会话期间查询到的数据会被放在当前会话的一级缓存中
Employee employee1 = mapper.getEmployeeById(6);
System.out.println(employee1);
System.out.println("----------");
//二级缓存存在:SqlSession关闭或者提交之后才会生效
session.commit();
Employee employee2 = mapper1.getEmployeeById(6);
System.out.println(employee2);
} catch (Exception e) {
// TODO: handle exception
}finally{
session.close();
}
}
测试结果
两个session对应同一个namespace,可以使用二级缓存,第一查询发送了sql语句,第二次没有发送sql语句,二级缓存生效了
2.4和缓存相关的配置
a. cacheEnabled=true/false: 设置为false后,关闭的是二级缓存. 一级缓存依旧可以使用。
<settings>
<!-- 配置使用二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
b. 每个select标签都有 useCache="true": 设置为false后,关闭的是二级缓存,一级缓存依旧可以使用. (一般是true)
<!--useCache:使用二级缓存 -->
<select id="getEmployeeById" resultType="employee" useCache="true">
select * from
tbl_employee where id=#{id}
</select>
c. 每个增删改标签都有 flushCache="true": 增删改操作后,会清空一二级缓存,每个select标签也有 flushCache="false": 每次查询完之后,不会刷新缓存,查询到的数据将不放到二级缓存
<!--useCache:使用二级缓存 -->
<select id="getEmployeeById" resultType="employee" flushCache="flase">
select * from
tbl_employee where id=#{id}
</select>
<!-- 增删改之后清空一二级缓存 -->
<delete id="deleteEmployeeById" flushCache="true">
delete from
tbl_employee where id=#{id}
</delete>
d. session.clearCache(): 只会清空当前的一级缓存,二级缓存仍然可以使用。
e. localCacheScope : 本地缓存的作用域
SESSION: 当前会话的数据保存在会话缓存中.
STATEMENT: 相当于可以禁用一级缓存.
3.第三方缓存整合
EhCache 是一个纯Java的进程内的缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。MyBatis定义了Cache接口方便我们进行自定义扩展。
3.1使用步骤
1、导入ehcache包,以及整合包,日志包
2、编写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="D:\zcy\ehcache" />
<defaultCache
maxElementsInMemory="1000"
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(先进先出)
-->
3、SQL映射文件配置cache标签
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
4、自定义类实现序列化接口
参照缓存:若想在命名空间中共享相同的缓存配置和实例。可以使用 cache-ref 元素来引用另外一个缓存。
-缓存机制MyBatis-缓