0%

【Mybatis】缓存机制

Mybatis缓存机制学习笔记

一、介绍

  • Mybatis包含一个非常强大的查询缓存机制,它可以非常方便地配置和定制,缓存可以极大提升查询效率
  • Mybatis默认包含两级缓存
    • 一级缓存(本地缓存)和二级缓存(全局缓存)
      • 1、默认情况下,只有一级缓存开启
      • 2、二级缓存需要手动开启和配置
      • 3、为了提高拓展性。Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存

二、一级缓存

2.1 介绍

  • 一级缓存(本地缓存),也被称为sqlSession级别的缓存,一级缓存是一致开启的无法关闭。
  • 在与数据库同一次会话期间,查询到的数据会放在本地缓存中
  • 以后如果需要获取相同的数据,会支架从缓存中拿,没必要再去查询数据库

2.2 演示实例

  • 前期准备:
    • 为了能够更好展现缓存的效果,要导入Mybatis下的log4j-1.2.17.jar日志包
    • 并把日志包的配置文件log4j.xml放在src目录下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
日志包配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
  • 测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
测试方法
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("./Dao/MybatisConfig.xml");

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);

SqlSession ss = ssf.openSession();

EmployeeMapper mapper = ss.getMapper(EmployeeMapper.class);

//第一次查询sql,向数据库发送了sql语句
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);

//第二次查询,并没有向数据库发送sql语句
Employee emp02 = mapper.getEmpById(1);
System.out.println(emp02);

ss.close();
}
  • 测试结果分析:
    • 在测试方法类,共执行了两次sql语句,但是想数据库发送语句只出现了一次
    • 而第二次查询并没有发送sql语句
    • 证明了是直接使用缓存,发现缓存有此数据,于是直接拿来使用

2.3 不能使用一级缓存情况

  • (1)sqlSession改变了,不是同一个sqlSession
  • (2)sqlSession一样,但查询条件不一样
  • (3)sqlSession一样,但在两次查询期间,执行了增删改的操作
  • (4)sqlSession一样,被手动清除掉缓存,执行openSession.clearCache();方法

三、二级缓存

3.1 介绍

  • 二级缓存(全局缓存):基于namespqce级别的缓存
    • 一个名称空间对应一个二级缓存
  • 工作机制:
    • (1)一个会话,查询一条数据。这条数据就会放在当前会话的以及缓存中,即在一级缓存
    • (2)如果会话关闭;一级缓存中的数据会被保存在二级缓存中;新的会话查询信息,就可以查找二级缓存信息
    • 不同namespace(映射文件)查询的数据会在放在自己对应的缓存中(map中)

2.2 开启步骤

  • 开启二级缓存步骤
  • (1)在全局配置文件中设置
    • <setting name="cacheEnabled" value="true"/>:开启二级缓存
1
2
3
4
5
6
7
全局配置文件
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/><!--开启驼峰命名-->
<setting name="lazyLoadingEnabled" value="true"/><!--开启所有关联对象都会延迟加载-->
<setting name="aggressiveLazyLoading" value="false"/><!--关闭所有对象全部加载-->
<setting name="cacheEnabled" value="true"/><!--开启二级缓存-->
</settings>
  • (2)去每个mapper.xml(映射文件)中配置使用二级缓存
    • <mapper>标签下使用<cache></cache>开启
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
映射文件
<mapper namespace="Dao.EmployeeMapper">
<!-- 配置二级缓存
<cache eviction="" flushInterval="" readOnly="" size="" type=""></cache>
eviction:缓存回收策略
LRU - 最近最少使用:移除最长时间不被使用的对象
FIFO - 先进先出:按对象进入缓存的顺序来移除
SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用:更加积极地移除基于垃圾收集器状态和弱引用状态的对象
默认使用:LRU
flushInterval:缓存刷新间隔
缓存多长时间清空一次
默认不清空
readOnly:是否只读
true:只读,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据
mybatis会加快获取速度,直接将数据在缓存中的引用交给用户
false:非只读,mybatis觉得获取的数据可能会被修改,
mybatis会利用序列化/反序列化的技术克隆一份新的数据给你。安全,速度。
默认:false
size:缓存存放多少元素
type:指定自定义缓存的全类名
自定义缓存:实现Cache接口
-->
<!--使用二级缓存-->
<cache></cache>

<select id="getEmpById" resultType="Dao.Employee" useCache="true">
select id, last_name lastName, gender, email
from tbl_employee
where id = #{id}
</select>

</mapper>
  • (3)查询封装的JavaBean实现序列化/反序列化接口
    • public class Xxxx implements Serializable
1
2
3
4
JavaBean
public class Employee implements Serializable {
类中内容省略
}

3.3 演示实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
测试方法
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("./Dao/MybatisConfig.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);

//开启第一个sqlSession
SqlSession ss1 = ssf.openSession();
EmployeeMapper mapper1 = ss1.getMapper(EmployeeMapper.class);
Employee emp1 = mapper1.getEmpById(1);
System.out.println(emp1);
//将sqlSession关闭,一级缓存内容转移到二级缓存
ss1.close();

//开启第二个sqlSession,与第一个sqlSession执行同一sql语句
SqlSession ss2 = ssf.openSession();
EmployeeMapper mapper2 = ss2.getMapper(EmployeeMapper.class);
Employee emp2 = mapper2.getEmpById(1);
System.out.println(emp2);
ss2.close();
}
  • 结果分析
    • 和一级缓存分析一样,只是向数据库发送了一次sql语句

3.4 和缓存有关的设置

  • (1)全局配置文件:cacheEnabled="true"/"false",开启/关闭二级缓存
  • (2)映射文件select标签:useCache="true"/"fasle",使用/不使用二级缓存,默认true
  • (3)每个增删改标签都有:flushCache="true"/"false",是否清除缓存,一二级都会被清空,默认true
    • 查询标签也有,默认为fasle,清空缓存对于查询没有意义,一般不修改
  • (4)openSession.clearCache(); 手动清除缓存,只会清掉一级缓存,不影响二级缓存
  • (5)全局配置文件:localCacheScope="SESSION"/"STATEMENT"本地缓存作用域(一级缓存作用域)
    • SESSION:缓存保存在当前会话
    • STATEMENT:可以禁用一级缓存

四、整合Ehcache缓存框架

4.1 自定义缓存介绍

  • Mybatis支持自定义缓存
    • 实现cache接口的类就可以作为自定义缓存
    • <cache type="自定义缓存全类名"></cache>设置
  • 但一般我们不会自定义缓存类,而是使用现有的缓存类,也是下面所说的ehcache
  • EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点

4.2 整合步骤

  • (2)在映射文件中使用ehcache自定义缓存类
  • (3)在src目录下,放置ehcache的配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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:\FrameWork\Ehcache\overData" />

<defaultCache
maxElementsInMemory="1"
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(先进先出)
-->
  • (4)若其他Mapper文件也要配置该缓存空间
    • 可以<cache-ref namespace=""></cache-ref>来进行配置
    • 引用名称空间,可以使用别人的名称空间,共用缓存

4.3 演示结果分析

  • 重新执行二级缓存的测试方法
  • 结果分析
    • ehcahe将二级缓存文件保存在本地,可以去本地查看