MyBatis(4) 缓存
2023-10-30 15:00:52 # Backend # MyBatis

1. 一级缓存

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问

使一级缓存失效的四种情况:

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存

一级缓存是默认开启

1
2
3
4
5
6
7
8
@Test
public void cacheTest() {
OrderMapper orderMapper = session.getMapper(OrderMapper.class);
Order order1 = orderMapper.selectOrderWithCustomerById(2);
System.out.println(order1);
Order order2 = orderMapper.selectOrderWithCustomerById(2);
System.out.println(order2);
}

image-20231027220655955

当两次查询中间执行一次插入操作, 一级缓存失效

1
2
3
4
5
6
7
8
9
10
@Test
public void cacheTest() {
OrderMapper orderMapper = session.getMapper(OrderMapper.class);
Order order1 = orderMapper.selectOrderWithCustomerById(2);
System.out.println(order1);
Order order = new Order(null, "o5", new Customer(1, "c01", null));
orderMapper.insertOrder(order);
Order order2 = orderMapper.selectOrderWithCustomerById(2);
System.out.println(order2);
}

image-20231028165357327

2. 二级缓存

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取

二级缓存开启的条件:

  • 在核心配置文件中,设置全局配置属性cacheEnabled=”true”,默认为true,不需要设置
  • 在映射文件中添加标签<cache />
  • 二级缓存必须在SqlSession关闭或提交之后有效
  • 查询的数据所转换的实体类类型必须实现序列化的接口

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testTwoCache(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
OrderMapper mapper1 = sqlSession1.getMapper(OrderMapper.class);
System.out.println(mapper1.selectOrderWithCustomerById(1));
sqlSession1.close();

SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
OrderMapper mapper2 = sqlSession2.getMapper(OrderMapper.class);
System.out.println(mapper2.selectOrderWithCustomerById(1));
sqlSession2.close();
} catch (IOException e) {
e.printStackTrace();
}
}

在 OrderMapper.xml 中添加 <cache/>

image-20231028203039622

Order, Customer类实现 Serializable 接口

测试结果

image-20231028202841386

2.1 MyBatis缓存查询的顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
  • 如果二级缓存没有命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭之后,一级缓存中的数据会写入二级缓存

3. 整合第三方缓存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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<dependencies>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.1.0</version>
</dependency>
<!-- log4j2日志 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.21.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.21.0</version>
</dependency>
<!-- log4j2适配slf4j -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.21.0</version>
<scope>test</scope>
</dependency>
<!-- slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<!-- Mybatis EHCache整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>

创建EHCache的配置文件ehcache.xml

  • 名字必须叫ehcache.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\ehcache"/>

<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>

在核心配置文件中,设置全局配置属性cacheEnabled=”true”,默认为true,不需要设置

mapper文件配置

1
2
3
4
5
<!--默认的二级缓存-->
<!-- <cache/> -->

<!--使用EhcacheCache缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

配置文件说明

属性名 是否必须 作用
maxElementsInMemory 在内存中缓存的element的最大数目
maxElementsOnDisk 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal 设定缓存的elements是否永远不过期。 如果为true,则缓存的数据始终有效, 如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断
overflowToDisk 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
timeToIdleSeconds 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
diskPersistent 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false
diskExpiryThreadIntervalSeconds 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s, 相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy 当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。 默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)

测试结果

image-20231028235227770