继续用提问的方式来看 Mybatis 的缓存设计.
1,Mybatis 如何开启缓存
Mybatis 对查询结果进行缓存, 所以缓存的对象为具体的 Statement
通过在 Statement 上是否使用缓存来启用.
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap" fetchSize="5" statementType="PREPARED" useCache="true">
useCache 默认值为 true
2, 谁持有缓存?
Mybatis 中有两个对象持有缓存.
CachingExecutor 以及 BaseExecutor
CachingExecutor 和 SimpleExecutor 的关系是持有的关系
- public class CachingExecutor implements Executor {
- private final Executor delegate;
- private final TransactionalCacheManager tcm = new TransactionalCacheManager();
3, 缓存何时开启?
当开启 Mapper 级别的缓存时, 缓存被开启
缓存是一直开启的
4, 缓存何时被命中?
5, 缓存清空的时机
1数据库发生变更 (update,insert,delete), 即使事物没有提交, 缓存也失效
2对于相同命名空间 Mapper 的缓存, 多个线程, 并发使用相同 Mapper 的不同语句, 任意一个执行事物提交, 可导致缓存失效
实际效果:
缓存会和 MySQL 的 rr 隔离级别一致, 不会导致幻读 (read uncommited)
缓存在不使用缓存的时候, 有效
验证缓存
使用代码验证:
两次查询, 只访问了数据库 1 次. 第二次不再请求数据库
一次查询, 一次更新 (不提交), 一次查询. 缓存失效, 出发了两次数据库查询.
缓存的验证
不配置缓存时, 验证不同的 sqlSesiion 缓存是否通用
触发 2 次数据库访问
配置缓存参数
- <settings>
- <setting name="cacheEnabled" value="true"/>
- </settings>
- <cache/>
结果出乎意料, 缓存竟然没有命中. 分析下原因:
即使打开了缓存, 首先也是保存在事物级别的缓存里, TransactionalCache 里.
只有当实物提交时, 才会真正将缓存转移到 delegate 中 (delegate 是真正的 Mapper 之间公用的缓存)
调整代码, 后缓存生效.
让人不理解的, 出于什么目的, 要 commit 后, 再共享?
如果在某一秒, 有 10 个并发共同访问数据库. 那他们会同时产生 10 个连接, 并不会命中.
6,Mybatis 缓存设计的目的
简单, 不配置也可以使用. 用与在一个 sqlSession 中多次使用相同条件多次查询.
7,Mybatis 缓存设计的目的
增强版, 也支持事物级别的缓存, 同时支持不同 sqlSession 之间的共享. 扩大可缓存的可用性.
来源: https://www.cnblogs.com/marioS/p/10353468.html