MyBatis一级缓存会话级失效

wen java案例 2

本文目录导读:

MyBatis一级缓存会话级失效

  1. 执行了增删改操作(最核心原因)
  2. 调用了 SqlSession.clearCache() 方法
  3. 查询条件不同
  4. 使用了 flushCache=true 的查询
  5. 跨越了不同的 SqlSession 实例
  6. 查询结果被映射到不同的 ResultMap 或对象类型
  7. 总结:什么时候一级缓存会“失效”?
  8. 如何判断是不是“真的失效”?

MyBatis 的一级缓存是 SqlSession 级别的缓存,默认开启,且无法关闭(但可以手动清空或使其失效)。

你提到的“会话级失效”,通常不是指缓存功能本身关闭,而是指在同一个 SqlSession 中,后续相同的查询没有命中缓存,导致又发了一次 SQL

以下是导致 MyBatis 一级缓存(会话级)失效的几种常见原因:

执行了增删改操作(最核心原因)

这是最常见的“失效”场景,在同一个 SqlSession 中,如果先执行了查询,然后执行了 insertupdatedelete 操作(无论操作的是哪张表,影响的是哪条数据),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 或对象类型

如果两个查询的 resultTyperesultMap 定义不同,MyBatis 也会认为这是不同的缓存条目。

什么时候一级缓存会“失效”?

情况 是否失效 原因
执行了 insert/update/delete 事务性操作强制清空缓存以保证数据一致性
调用了 session.clearCache() 手动清空
更换了 SqlSession 跨会话,缓存自然不共享
查询参数不同 缓存 key 不同
配置了 flushCache=true 每次查询前清空缓存
查询同一数据,未增删改,同一session 这是正常命中缓存的情况

如何判断是不是“真的失效”?

最简单的办法:在代码中打印日志,观察 Preparing: 开头的 SQL 日志出现了几次,如果出现了两次相同的 SQL,说明第一次的缓存未被第二次使用,即“失效”了,根据上述原因排查即可。

抱歉,评论功能暂时关闭!