mybatis一级缓存默认绑定sqlsession,不可关闭,生命周期与sqlsession一致:创建时初始化,close()或clearcache()时清空或释放;执行dml、commit()、rollback()或跨sqlsession查询均导致失效,其cachekey包含mapper id、sql、参数及分页等信息。

MyBatis 一级缓存默认绑定在 SqlSession 实例上,它不是全局共享的,也不依赖配置开关——只要用了 SqlSession,缓存就自动存在。它的生命周期和失效逻辑非常明确:从 SqlSession 创建开始,到其被关闭(close())或显式清空(clearCache())为止;中间若发生任何可能影响数据一致性的操作,缓存也会立即失效。
一级缓存的默认生命周期
一级缓存本质上是 SqlSession 内部 Executor 持有的一个 PerpetualCache 对象(底层是 HashMap),只在当前 SqlSession 实例存活期间有效:
- SqlSession 创建时,缓存对象同步初始化
- 所有查询结果按
CacheKey(含 Mapper ID、SQL 语句、参数、分页信息等)存入该缓存 - SqlSession 调用
close()后,缓存对象被释放,数据不可再访问 - SqlSession 调用
clearCache(),仅清空缓存内容,对象仍可继续使用
一级缓存失效的五种典型边界条件
缓存不是“过期”而是“被主动清空”,只要触发以下任一动作,当前 SqlSession 中所有缓存条目都会被清除:
- 执行任意 DML 操作:即 insert / update / delete 语句提交后,缓存立刻清空(防止脏读)
- 调用 commit() 方法:即使没执行 DML,显式 commit 也会触发清空(事务边界语义)
- 调用 close() 方法:SqlSession 结束,缓存连同 Executor 一并销毁
- 调用 clearCache() 方法:手动清空,常用于规避缓存干扰的测试或特殊业务场景
- 不同 SqlSession 实例之间完全隔离:哪怕查的是同一 SQL 和参数,另一个 SqlSession 的缓存不会命中,也不影响当前缓存
为什么查询条件稍有变化就不会命中缓存?
一级缓存的 key 是由 CacheKey 严格生成的,包含:Mapper 全限定名 + SQL 字符串 + 参数值 + 行bounds(分页) + 环境ID。所以:
- 两次查询参数不同(如
id=1vsid=2),key 不同 → 不命中 - SQL 中字符串拼接导致空格/换行差异,也会造成 key 不一致
- 使用了
<bind></bind>或动态 SQL 但参数未变,一般仍能命中;但若生成的最终 SQL 字符串不同,就无法复用
实际开发中容易忽略的关键点
一级缓存虽默认开启、无需配置,但它的“透明性”反而容易引发问题:
- Spring 整合 MyBatis 时,
@Transactional方法内默认复用同一个 SqlSession,缓存持续有效;但方法退出后 SqlSession 通常被关闭,缓存自然消失 - 手写工具类中反复
openSession()又不close(),不仅缓存无效,还会泄漏连接 - 误以为“同一 Mapper 接口多次查询”就能复用缓存,却忽略了每次 new SqlSession 就等于新开一个缓存空间
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











