MyBatis 缓存详解:二级缓存配置与查询流程
·
一、二级缓存核心特性
MyBatis 二级缓存存储的不是 Java 对象本身,而是对象的序列化数据。这意味着从二级缓存中获取的对象,与原对象不是同一个实例(用==比较返回false),但对象内容一致。
代码示例:验证二级缓存存储特性
public class User implements Serializable {
private Integer id;
private String username;
private String password;
}
// 测试方法
@Test
public void testSecondLevelCache() {
// 第一个SqlSession查询并提交(触发一级缓存写入二级缓存)
User user1 = null;
try (SqlSession session1 = MyBatisUtil.getSqlSession()) {
UserMapper mapper1 = session1.getMapper(UserMapper.class);
user1 = mapper1.selectById(1);
session1.commit();
}
// 第二个SqlSession从二级缓存查询
try (SqlSession session2 = MyBatisUtil.getSqlSession()) {
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user2 = mapper2.selectById(1);
System.out.println("对象实例是否相同:" + (user1 == user2)); // false
System.out.println("对象内容是否相同:" + user1.equals(user2)); // true
}
}
二、二级缓存关键配置
在 Mapper.xml 中通过<cache>标签配置二级缓存,核心参数如下:
| 参数 | 作用 | 默认值 |
|---|---|---|
eviction | 回收策略:LRU(最近最少使用)/FIFO(先进先出) | LRU |
size | 缓存最大存储条数 | 1024 |
flushInterval | 缓存自动刷新间隔(毫秒),0 表示不自动刷新 | 0 |
readOnly | 读写模式:true(只读,性能高)/false(读写,安全) | false |
配置示例(UserMapper.xml)
<!-- 开启并配置二级缓存 -->
<cache
eviction="LRU" <!-- 回收策略:移除最久未使用的缓存 -->
size="512" <!-- 最多存512条数据 -->
flushInterval="60000" <!-- 60秒自动刷新缓存 -->
readOnly="false"/> <!-- 读写模式,返回对象拷贝 -->
<!-- 查询语句需开启缓存(useCache="true") -->
<select id="selectById" resultType="User" useCache="true">
SELECT id, username, password FROM user WHERE id = #{id}
</select>
全局开关(mybatis-config.xml)
二级缓存需先开启全局配置(默认开启,可显式配置):
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
三、MyBatis 缓存查询顺序
MyBatis 查询遵循 “先二级、再一级、最后数据库” 的逻辑:
- 发起查询时,先检查二级缓存(跨 SqlSession 生效);
- 二级缓存未命中,检查一级缓存(当前 SqlSession 内生效);
- 一级缓存也未命中,执行 SQL 查询数据库;
- 查询结果写入一级缓存,当 SqlSession 关闭 / 提交时,一级缓存数据写入二级缓存。
代码示例:验证查询顺序
@Test
public void testCacheQueryOrder() {
// 第一次查询:无缓存,查数据库,结果写入一级缓存
try (SqlSession session1 = MyBatisUtil.getSqlSession()) {
UserMapper mapper1 = session1.getMapper(UserMapper.class);
System.out.println("第一次查询:" + mapper1.selectById(1)); // 查数据库
session1.commit(); // 一级缓存写入二级缓存
}
// 第二次查询:命中二级缓存,不查数据库
try (SqlSession session2 = MyBatisUtil.getSqlSession()) {
UserMapper mapper2 = session2.getMapper(UserMapper.class);
System.out.println("第二次查询:" + mapper2.selectById(1)); // 查二级缓存
}
// 同一Session内查询:命中一级缓存
try (SqlSession session3 = MyBatisUtil.getSqlSession()) {
UserMapper mapper3 = session3.getMapper(UserMapper.class);
User u1 = mapper3.selectById(1);
User u2 = mapper3.selectById(1);
System.out.println("同一Session内对象是否相同:" + (u1 == u2)); // true
}
}
总结
- MyBatis 二级缓存存储序列化数据,跨会话获取的对象实例不同但内容一致;
- 二级缓存可通过
eviction、size等参数自定义,默认采用 LRU 回收策略、读写模式; - 缓存查询顺序为:二级缓存 → 一级缓存 → 数据库,SqlSession 提交 / 关闭时一级缓存写入二级缓存。
更多推荐



所有评论(0)