MyBatis一级缓存默认开启且无需配置,但仅在同一个SqlSession内、未执行增删改、SQL与参数完全一致时才命中;本质为SqlSession内置的PerpetualCache(HashMap实现),随SqlSession关闭或事务提交而失效。

MyBatis 一级缓存默认就开启,不需要任何配置,但它的复用有明确前提:必须在同一个 SqlSession 内、未执行增删改操作、SQL 和参数完全一致。
一级缓存默认生效的条件
它本质是 SqlSession 内置的 PerpetualCache(基于 HashMap),生命周期与 SqlSession 绑定。只要满足以下三点,第一次查库后,第二次相同查询就会命中缓存:
- 使用的是同一个
SqlSession实例(不是新建的) - 两次
select的MappedStatement ID(即 Mapper 方法全路径)完全相同 - 参数值、类型、顺序、分页参数(
RowBounds)全部一致——例如Integer(1)和String("1")会生成不同CacheKey,不命中
为什么有时“开了却没用”
常见失效场景不是配置问题,而是运行时行为导致缓存被清空或无法延续:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 中间执行了
insert/update/delete:当前SqlSession的所有一级缓存立即清空 -
SqlSession被关闭或提交事务(commit)、回滚(rollback):缓存随会话销毁 - Spring 环境下无
@Transactional:每个 DAO 方法调用都新建SqlSession,缓存无法跨方法复用 - 用了
try-with-resources或手动close():会话提前结束,缓存失效
如何让一级缓存稳定复用
重点不在“开启”,而在“维持会话上下文”。推荐做法:
立即学习“Java免费学习笔记(深入)”;
- 在 Spring 中对读多写少的业务方法加上
@Transactional(readOnly = true):确保整个方法内复用同一个SqlSession - 避免在一次会话中混用读和写操作;如需写后继续读,考虑手动
clearCache()后重新查,而非依赖旧缓存 - 统一参数类型,比如 ID 入参固定用
Long,不用String或Integer混用 - 分页查询要留意
RowBounds:offset/limit 不同 → 缓存 key 不同 → 不复用;可改用数据库分页或逻辑分页规避
验证是否真正在工作
打开 MyBatis 日志最直接:
- 在
application.yml加:logging.level.org.apache.ibatis=DEBUG - 运行后观察日志中是否有类似 [Cache Hit Ratio [com.example.mapper.UserMapper]: 0.5] 的提示
- 若始终显示
0.0,优先检查是否每次查询都新建了SqlSession,或中间夹了写操作

















