开,但仅限同一SqlSession内且未执行增删改操作;本质是SqlSession内置的PerpetualCache,随session关闭或事务提交而失效,无需配置,默认启用。

MyBatis 一级缓存默认到底开没开?
开,但只在同一个 SqlSession 内有效,且必须满足「未执行过增删改操作」这个隐藏前提。很多人以为只要没手动关,缓存就一直稳稳工作,结果发现两次 select 还是查了两次库——八成是中间夹了个 insert、update 或 delete,触发了缓存清空。
一级缓存本质是 SqlSession 实例内部的 PerpetualCache(基于 HashMap),生命周期与 SqlSession 绑定。一旦 SqlSession 关闭或提交事务(commit)、回滚(rollback),缓存就失效。
- 不需任何配置,默认启用,无需设
cacheEnabled=true(那是二级缓存的开关) -
select语句必须完全相同:相同的StatementId+ 相同的参数(包括参数类型、顺序、值) - 缓存键(key)由
MappedStatement的 id 和参数经CacheKey计算生成,不是简单字符串拼接 - 使用
RowBounds分页时,即使 SQL 相同,offset/limit不同 → 缓存 key 不同 → 不命中
为什么 SqlSession 一提交/关闭缓存就没了?
因为一级缓存设计定位就是「单次请求内快速复用」,不是跨请求共享。MyBatis 认为:事务提交意味着数据可能已变更,继续用旧缓存风险太高;SqlSession 关闭则整个上下文销毁,缓存自然释放。
典型翻车场景:try-with-resources 自动关闭 SqlSession,或者 Spring 管理的 SqlSessionTemplate 在每次 DAO 方法调用后自动回收 —— 此时一级缓存基本等于没用。
- Spring 中,每个
@Transactional方法通常对应一个新SqlSession,除非显式传播为REQUIRES_NEW或复用同一 session - 手动管理
SqlSession时,若在一次 session 中反复查同一数据,缓存有效;但换一个 session 就重来 - 调用
sqlSession.clearCache()会主动清空,但一般没必要,增删改已自动处理
怎么验证一级缓存是否生效?
最直接的办法:打开 MyBatis 日志,看是否出现 Cache Hit Ratio 提示,或观察 SQL 是否重复打印。注意日志级别要设为 DEBUG,且确保没被其他日志框架拦截。
更稳妥的是加断点或日志打点,在 Executor 的 query 方法入口处观察是否跳过了数据库查询逻辑(比如直接从 localCache 取值)。
- Spring Boot 项目可在
application.yml加:logging: level: org.apache.ibatis: DEBUG - 看到类似
[Cache Hit Ratio [com.example.mapper.UserMapper]: 0.5]表示命中率 50%,说明缓存正在参与 - 如果始终显示
0.0,先检查是否用了不同SqlSession实例,再确认有没有无意识的写操作干扰
一级缓存和二级缓存能共存吗?
能,但它们互不干扰:一级缓存优先级更高,查不到才去查二级缓存。不过二级缓存默认关闭,且需要显式配置 <cache/> 标签或 @CacheNamespace 注解,还要求实体类实现 Serializable。
关键区别在于作用域:一级是 SqlSession 级,二级是 Mapper 级(即 namespace 级),多个 SqlSession 可共享同一二级缓存,但一级永远隔离。
- 开启二级缓存后,一级缓存行为不变,该清还清,该命中的继续命中
- 二级缓存的更新策略(如
flushInterval、readOnly)不影响一级缓存逻辑 - 如果误把二级缓存当成一级来依赖(比如期望跨 session 复用),就会掉进一致性陷阱
SqlSession 生命周期和事务边界的误判。尤其在 Spring 环境下,session 是被框架悄悄管理的,你写的代码里看不到 open/close,但缓存效果全取决于它。










