MyBatis 缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。

一、缓存

1. 什么是缓存?

所谓缓存,就是存在内存中的临时数据。

将用户经常查询的数据放在缓存之中,当用户需要数据时,不再需要向数据库查询,而是可以直接从内存中获取。

2. 缓存的作用

  • 减少了与数据库的交互次数
  • 提高了查询效率

3. 适用于缓存的数据

经常查询且不经常改变的数据

二、MyBatis 缓存

MyBatis 内置了缓存机制,它可以方便地配置和定制。

在 MyBatis 中,默认定义了一级缓存和二级缓存,并且支持自定义二级缓存。

三、缓存的开启与关闭

1. 全局开关

MyBatis 默认开启了缓存,也可以通过配置文件显式地开启缓存:

  • 打开 mybatis-config.xml

  • 修改内容如下:

    1
    2
    3
    <settings>
    <setting name="cacheEnabled" value="true"/>
    </settings>

2. 语句开关

可以在 XxxMapper.xml 的语句标签中,使用 useCache 来配置单个语句是否使用缓存。

四、一级缓存

1. 说明

  • 又称为本地缓存
  • 默认情况下,一级缓存自动开启
  • MyBatis 会将一次 SqlSession 中查询到的数据缓存在本地,如果需要再次获取相同的数据时,无需再去查询数据库,而是直接从缓存中获取即可
  • 缓存仅在 SqlSession 中有效,无法被其它 SqlSession 获取,会随着 SqlSession 的关闭而销毁

2. 示例

(1) 一个 SqlSession 中

1
2
3
4
5
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.getUserListById(1);
User user2 = mapper.getUserListById(1);
sqlSession.close();

观察日志输出可以注意到:只向数据库查询了一次。

虽然调用了两次 getUserListById() 方法,但两次查询在同一个 SqlSession 中,并且获取的数据相同,因此直接从缓存中获取数据,不再向数据库查询。

(2) 不同 SqlSession 中

1
2
3
4
5
6
7
8
9
SqlSession sqlSession1 = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession1.getMapper(UserMapper.class);
User user1 = mapper.getUserListById(1);
sqlSession1.close();

SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.getUserListById(1);
sqlSession2.close();

观察日志输出可以注意到:向数据库查询了两次。

虽然两次查询获取的数据是相同的,但由于两次查询处在不同的 SqlSession 中,因此一级缓存无法起作用,仍然需要从数据库获取数据。

五、二级缓存

1. 说明

  • 又称为全局缓存
  • 基于 namespace,一个命名空间对应一个二级缓存
  • 缓存在 XxxMapper 中有效

2. 启用二级缓存

在 XxxMapper.xml 中,增加内容如下:

1
<cache/>

3. cache 标签的属性

  • eviction,清除策略,可选值有:

    • LRU:默认,总是移除最长时长不被使用的对象
    • FIFO:按先进先出原则移除对象
    • SOFT:基于垃圾回收器状态和软引用规则移除对象
    • WEAK:更积极地基于垃圾收集器状态和弱引用规则移除对象
  • flushInterval,刷新间隔

    经过该时间以后,将会自动刷新缓存

    默认情况下,flushInterval 的值为 0,不自动刷新,仅在调用语句时刷新

  • size,指定缓存的对象数量

    默认为 1024

  • readonly:设置缓存是否只读

    • 若设置为只读,获取时会返回缓存的对象实例,实例不能被修改,性能更好
    • 若设置为可读写,获取时会返回缓存的对象的拷贝,实例可以被修改,性能更差,更安全

    默认为可读写

  • type:填入全限定类名,用于自定义缓存

4. 示例

1
2
3
4
5
6
7
8
9
SqlSession sqlSession1 = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession1.getMapper(UserMapper.class);
User user1 = mapper.getUserListById(1);
sqlSession1.close();

SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.getUserListById(1);
sqlSession2.close();

虽然两次查询是在不同的 SqlSession 中,但由于二级缓存的作用域是全局,因此第二次查询可以直接从缓冲中获取,而无需向数据库查询。

5. 多个 mapper 共享缓存

正常情况下,缓存仅在 XxxMapper 中有效,如果希望在多个 Mapper 中共享缓存,可以使用 cache-ref 标签。

1
<cache-ref namespace="其它mapper的全限定类名"/>

七、自定义缓存

  • 在 XxxMapper.xml 中使用 cache 标签开启二级缓存
  • 使用 cache 标签的 type 属性指向缓存类(自己实现或导入第三方)

参考