MyBatis二级缓存需同时开启全局cacheEnabled=true和Mapper级@CacheNamespace或<cache/>;POJO须实现Serializable以防序列化异常;增删改操作默认清空对应Mapper全部缓存;缓存key冲突常见于同名参数或id重复,应确保select唯一id与明确@Param命名。

MyBatis 二级缓存开启后查不到数据?先确认 cacheEnabled 和 @CacheNamespace 都开了
二级缓存默认是关闭的,光在 XML 里加 <cache/> 或用注解标记接口,不打开全局开关没用。全局开关在 mybatis-config.xml 里:configuration.settings.cacheEnabled 必须设为 true(默认就是 true,但很多项目显式设成 false 却忘了改回来)。Mapper 接口或 XML 文件还得单独启用:接口上加 @CacheNamespace,或者 XML 的 <mapper> 根节点下加 <cache/>。
常见错误现象:
- 缓存没生效,每次查询都走 SQL —— 很可能是
cacheEnabled=false或 Mapper 没声明缓存 - 启用了缓存但更新操作后旧数据还在 —— 缓存没自动清,后面会说
为什么查出来的对象必须实现 Serializable?
MyBatis 二级缓存底层默认用 PerpetualCache,它内部用 HashMap 存对象引用;但一旦开启 flushInterval、用到 Cache 的装饰器(比如 ScheduledCache),或者集成 Redis/Ehcache 等外部缓存时,对象就得序列化——因为要跨线程、跨 Session、甚至跨 JVM 传输。MyBatis 不强制校验,但运行时报 NotSerializableException 就是这儿卡住的。
实操建议:
- 所有可能进二级缓存的 POJO,都让它们实现
Serializable,哪怕只是空接口 - 别依赖 IDE 自动生成
serialVersionUID,手写一个固定值(如private static final long serialVersionUID = 1L;),避免类结构微调后反序列化失败 - 如果用了 Lombok,加
@Data的同时补上implements Serializable,否则生成的类没实现该接口
select 命令能缓存,insert/update/delete 为什么自动清缓存?
MyBatis 对同一 Mapper 下的所有 insert/update/delete 语句,默认会清空该 Mapper 对应的整个二级缓存区域。这是硬编码逻辑,不是配置项,没法关。
影响和注意点:
- 如果你在
UserMapper里更新了用户头像,哪怕只改了一条记录,UserMapper下所有select缓存全失效 —— 包括查列表、查详情、查统计 - 想精细控制清除范围?只能自己写缓存插件,或换用外部缓存(如 Redis)并手动管理 key
- 多个 Mapper 共享同一张表?各自缓存互不影响,但数据一致性得靠业务层兜底
缓存 key 冲突:不同参数的 select 返回同一个结果?
MyBatis 默认用 MappedStatement.getId() + 参数(经 ParamNameResolver 处理后的 Map)生成缓存 key。但如果你的 select 方法参数是 Map 或无参,又或者用了 @Param 但 key 名重复,就容易撞 key。
典型场景:
- 两个方法都叫
selectUser,一个传id,一个传name,但都用@Param("param")—— 缓存 key 一样,互相污染 - XML 中
<select id="getUser">和<select id="getUserById">如果 namespace 相同,而参数结构雷同,也可能命中同一缓存块 - 使用
resultMap时字段别名冲突、嵌套查询未配置flushCache=false,也会导致意外击中旧缓存
最稳的做法:每个 select 方法用唯一 id,参数用明确的 @Param 名,避免裸 Map 传参。
缓存不是银弹,尤其是多模块共用 Mapper、动态 SQL 复杂、或对实时性敏感的场景——这时候绕开二级缓存,比花半天调 key 冲突更省事。










