本文目录导读:

- 执行了增删改操作(最核心原因)
- 调用了
SqlSession.clearCache()方法 - 查询条件不同
- 使用了
flushCache=true的查询 - 跨越了不同的
SqlSession实例 - 查询结果被映射到不同的 ResultMap 或对象类型
- 总结:什么时候一级缓存会“失效”?
- 如何判断是不是“真的失效”?
MyBatis 的一级缓存是 SqlSession 级别的缓存,默认开启,且无法关闭(但可以手动清空或使其失效)。
你提到的“会话级失效”,通常不是指缓存功能本身关闭,而是指在同一个 SqlSession 中,后续相同的查询没有命中缓存,导致又发了一次 SQL。
以下是导致 MyBatis 一级缓存(会话级)失效的几种常见原因:
执行了增删改操作(最核心原因)
这是最常见的“失效”场景,在同一个 SqlSession 中,如果先执行了查询,然后执行了 insert、update 或 delete 操作(无论操作的是哪张表,影响的是哪条数据),MyBatis 会清空当前 SqlSession 的一级缓存。
-
原因: MyBatis 无法精确判断增删改操作是否影响了已缓存的数据,为了数据一致性,选择“宁可错杀一千,绝不放过一个”,直接清空整个缓存。
-
示例:
SqlSession session = sqlSessionFactory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user1 = mapper.selectById(1); // 发SQL,结果存入一级缓存 User user2 = mapper.selectById(1); // 命中缓存,不发SQL // 执行了一次更新,即使更新的是另一条数据 mapper.updateName(2, "newName"); User user3 = mapper.selectById(1); // 缓存已被清空,重新发SQL
调用了 SqlSession.clearCache() 方法
手动清空一级缓存。
User user1 = mapper.selectById(1); // 发SQL session.clearCache(); // 手动清空 User user2 = mapper.selectById(1); // 缓存已空,重新发SQL
查询条件不同
一级缓存以 [namespace + sql语句 + 参数 + 分页条件] 作为 key,如果查询的 SQL 语句不同(哪怕是多了一个空格),或者参数不同,自然无法命中缓存。
- 示例:
mapper.selectById(1); // 缓存key: ...selectById, param:1 mapper.selectById(2); // 缓存key: ...selectById, param:2 -> 未命中
使用了 flushCache=true 的查询
在 <select> 标签上可以设置 flushCache="true",虽然这通常用于二级缓存,但在某些 MyBatis 版本或配置中,它也会影响一级缓存,使得每次查询前都清空本地缓存。
<select id="selectById" resultType="User" flushCache="true">
select * from user where id = #{id}
</select>
这种情况下,即使是同一个 SqlSession,每次 selectById 都会清空并重新查询。
跨越了不同的 SqlSession 实例
这是概念上的“失效”,一级缓存是 SqlSession 实例级别 的,如果你用 session1 查询,然后用 session2 查询同样的数据,它们使用的是不同的缓存,session2 自然无法命中 session1 的缓存。
-
示例:
SqlSession session1 = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession(); session1.getMapper(UserMapper.class).selectById(1); // session1的缓存 session2.getMapper(UserMapper.class).selectById(1); // session2的缓存为空,重新发SQL
查询结果被映射到不同的 ResultMap 或对象类型
如果两个查询的 resultType 或 resultMap 定义不同,MyBatis 也会认为这是不同的缓存条目。
什么时候一级缓存会“失效”?
| 情况 | 是否失效 | 原因 |
|---|---|---|
执行了 insert/update/delete |
是 | 事务性操作强制清空缓存以保证数据一致性 |
调用了 session.clearCache() |
是 | 手动清空 |
| 更换了 SqlSession | 是 | 跨会话,缓存自然不共享 |
| 查询参数不同 | 是 | 缓存 key 不同 |
配置了 flushCache=true |
是 | 每次查询前清空缓存 |
| 查询同一数据,未增删改,同一session | 否 | 这是正常命中缓存的情况 |
如何判断是不是“真的失效”?
最简单的办法:在代码中打印日志,观察 Preparing: 开头的 SQL 日志出现了几次,如果出现了两次相同的 SQL,说明第一次的缓存未被第二次使用,即“失效”了,根据上述原因排查即可。