MyBatis一级缓存默认开启但仅限同一SqlSession内生效,跨SqlSession、执行增删改操作、参数对象hash不一致或事务传播行为变更(如REQUIRES_NEW)均会导致缓存未命中或被清空。

MyBatis 一级缓存默认开启,但实际开发中“查了两次还走数据库”很常见。根本原因不是缓存关了,而是缓存压根没命中或被清掉了——关键得看 SqlSession 是否一致 和 中间有没有触发清空动作。
同一请求里缓存不生效?先盯住 SqlSession 生命周期
一级缓存只在同一个 SqlSession 内有效。Spring Boot 默认用 @Transactional 管理 SqlSession,事务方法内多次查询通常能命中;但一旦跨方法、跨线程、或用了 REQUIRES_NEW 这类传播行为,就会创建新 SqlSession,旧缓存自然不可见。
- 事务方法内调用另一个
@Transactional(propagation = Propagation.REQUIRES_NEW)方法 → 新建 SqlSession,缓存隔离 - Service 方法没加
@Transactional,每次调用都走新 SqlSession(如非事务的查询接口) - 手动调用
sqlSessionFactory.openSession()创建会话,未复用 Spring 管理的实例
刚查完马上又查,结果还是打库?检查有没有隐式写操作
只要执行过任意 INSERT/UPDATE/DELETE(哪怕改的是其他表),MyBatis 就会清空当前 SqlSession 的整个一级缓存。这不是按 key 清除,是全量清空。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- Mapper 接口里调了
updateById()或insert(),哪怕在同个事务里,后续查询也失效 - MyBatis-Plus 的
selectOne()默认带flushCache=true,查完立刻清缓存(XML 中显式配置<select flushCache="true">同理) - 用了
SqlSessionBatch批量操作,它和普通 SqlSession 缓存互不感知
参数看着一样,缓存就是不认?确认对象一致性
缓存 key 是由 SQL 模板 + 参数对象的 hashCode() 和 equals() 共同决定的。基础类型(如 Long、Integer)没问题,但自定义对象或包装类容易踩坑。
立即学习“Java免费学习笔记(深入)”;
-
selectById(1L)和selectById(new Long(1)):后者新建对象,hashCode()不同 → 缓存 key 不同 - DTO 或 QueryWrapper 作为参数时,若未重写
equals()和hashCode(),即使字段值相同,key 也可能不一致 - 使用 Lombok 的
@Data时注意:若字段含null值或集合,生成的equals()行为需验证
怎么快速定位是不是一级缓存问题?三步排查法
别猜,靠日志和监控直击本质:
- 打开 MyBatis 日志(
logging.level.org.apache.ibatis=DEBUG),观察相同参数的 SQL 是否重复打印Preparing:和Parameters:—— 重复即未命中缓存 - 检查对应 Mapper 方法是否被
@Transactional包裹,再看调用链中是否有REQUIRES_NEW或非事务方法打断会话 - 搜代码里有没有
clearCache()、flushCache="true"、批量操作(ExecutorType.BATCH)、或增删改语句紧邻查询

















