MyBatis二级缓存要求实体类实现Serializable接口,是因为其跨SqlSession、跨线程甚至跨JVM的共享特性,启用flushInterval、淘汰策略或集成Redis等外部缓存时必须序列化;而一级缓存仅限单个SqlSession内存,无需序列化。

MyBatis 二级缓存要求实体类实现 Serializable 接口,不是为了“强制校验”,而是由其底层缓存机制和运行时行为决定的——不实现,很可能在运行中抛 NotSerializableException。
为什么二级缓存需要序列化
二级缓存是跨 SqlSession、跨线程甚至跨 JVM 的共享缓存。虽然默认使用 PerpetualCache(基于 HashMap 存引用),但一旦启用以下任一配置,对象就必须可序列化:
-
设置了
flushInterval:MyBatis 会用ScheduledCache包装,该装饰器内部会序列化对象做定时清理 -
配置了淘汰策略(如
LruCache):部分实现会触发深拷贝或状态快照,依赖序列化 - 集成外部缓存(Redis/Ehcache/Caffeine):数据需跨进程传输,必须序列化
-
开启
useCache="true"且存在多线程并发读写:MyBatis 缓存装饰链可能隐式调用序列化逻辑
不实现 Serializable 的典型报错场景
常见错误不是启动失败,而是在首次写入缓存或定时刷新时爆发:
- 日志出现
java.io.NotSerializableException: com.example.User - 异常堆栈里含
ScheduledCache、SerializedCache或第三方缓存客户端调用链 - 现象:查询成功一次后,第二次查就卡住或报错,缓存完全失效
实操建议:怎么正确实现
别只图编译通过,要兼顾稳定性与兼容性:
立即学习“Java免费学习笔记(深入)”;
- 所有可能进二级缓存的 POJO,统一
implements Serializable,哪怕只是空接口 -
手写固定
serialVersionUID,例如:private static final long serialVersionUID = 1L;;避免 IDE 自动生成导致类结构微调后反序列化失败 - 若用 Lombok,
@Data不自动加Serializable,需显式写:public class User implements Serializable { @Data ... } - 注意嵌套对象:如果实体包含其他自定义对象(如
Address、Role),它们也必须实现Serializable
一级缓存为什么不需要
一级缓存作用域仅限单个 SqlSession 内存,生命周期短、不跨线程、不持久化,全程用对象引用传递,完全不涉及序列化过程。所以它对 Serializable 零要求。


















