mybatis一级缓存基于非线程安全的hashmap实现,键为cachekey(含statementid、参数、rowbounds、sql模板),值为查询结果,生命周期绑定sqlsession,增删改操作会清空整个缓存。

MyBatis 用 PerpetualCache 实现一级缓存(也参与二级缓存基础构建),它的核心就是靠一个 非线程安全的 HashMap<object object></object> 来存数据,不加过期、不自动清理、不分布式同步——简单直接,但正因如此,它只适合在明确生命周期可控的场景里用。
PerpetualCache 的 HashMap 是怎么工作的
-
缓存键(key)不是原始 SQL 字符串或参数,而是 MyBatis 封装的
CacheKey对象CacheKey内部会把以下要素按固定顺序和规则哈希组合:
Alibabacloud Sdk Client Initialization For Java下载在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- Mapper 的
statementId(如com.example.UserMapper.selectById) - SQL 参数值(经过序列化/哈希处理,避免对象引用问题)
- 分页参数
RowBounds(offset/limit) - SQL 语句模板(带
?占位符的原始字符串)
这样能保证“逻辑相同”的查询生成相同的CacheKey,从而命中缓存。
- Mapper 的
缓存值(value)是查询结果,比如
List<user></user>或单个User对象
直接放进HashMap,不做深拷贝、不序列化、不校验类型——取出来就原样返回。-
所有操作都委托给
HashMap原生方法:-
putObject(key, value)→cache.put(key, value) -
getObject(key)→cache.get(key) -
removeObject(key)→cache.remove(key) -
clear()→cache.clear()
-
为什么敢用 HashMap 而不怕并发问题
-
PerpetualCache在一级缓存中,绑定在SqlSession内部的Executor.localCache上 -
SqlSession本身不是线程安全的,官方明确要求:每个线程应持有独立的SqlSession实例 - 所以
PerpetualCache天然处于单线程访问上下文,无需锁、无需并发容器 - 它的
ReadWriteLock字段(见源码)其实是为装饰器模式预留的,自身并不使用
注意几个关键事实
- 它不会自动失效:SQL 执行后写入缓存,除非显式调用
clear()、SqlSession.close()、SqlSession.commit()/rollback()(一级缓存默认清空),否则一直留着 - 它不区分读写:更新操作(insert/update/delete)默认会清空当前
SqlSession的整个一级缓存(通过localCache.clear()),防止脏读 - 它不支持自定义淘汰策略:LRU、FIFO、TTL 等能力要靠外层装饰器(如
LruCache,ScheduledCache)叠加实现
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










