CachingExecutor 执行器内部创建一个TransactionalCacheManager 事务缓存管理,并使用delegate 指向基础Executor
public class CachingExecutor implements Executor { //目标Executor private Executor delegate; private TransactionalCacheManager tcm = new TransactionalCacheManager(); public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } }
当开启二级缓存的情况下执行sqlsession的select方法时,首先会执行CachingExecutor的query方法。
public <E> List<E> query(...) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(...); } public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //获取二级缓存 Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms);//是否清空缓存 if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, parameterObject, boundSql); //先查询二级缓存 List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { //二级缓存没有,交给基础执行器Executor 去执行查询操作 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //放入预缓存中。 tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
流程:
- 先获取二级缓存。
- 判断当前SQL是否开启二级缓存。
- 开启的情况下,先去二级缓存查询。
- 二级缓存有,直接返回
- 二级缓存没有,交给基础执行器,走一级缓存执行过程。并把直接结果放入事务预缓存区。
事务型预缓存
在二级没有数据的情况下,通过BaseExecutor从数据库中查询到结果后,并没有直接放入二级缓存。而是先放入的事务预缓存中。
tcm.putObject(cache, key, list);
来看看这个预缓存区,如何工作。
//事务缓存管理者 public class TransactionalCacheManager { private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>(); public void putObject(Cache cache, CacheKey key, Object value) { getTransactionalCache(cache).putObject(key, value); } private TransactionalCache getTransactionalCache(Cache cache) { TransactionalCache txCache = transactionalCaches.get(cache); if (txCache == null) { txCache = new TransactionalCache(cache); transactionalCaches.put(cache, txCache); } return txCache; } } //事务预缓存 public class TransactionalCache implements Cache { private Cache delegate; private boolean clearOnCommit; private Map<Object, Object> entriesToAddOnCommit; private Set<Object> entriesMissedInCache; public void putObject(Object key, Object object) { entriesToAddOnCommit.put(key, object); } }
事务型缓存TransactionalCache,也可以理解为预缓存区,是通过装饰器模式设计的预缓存,通过delegate属性指向二级缓存,他使得二级缓存具有事务特性。
TransactionalCache 由TransactionalCacheManager事务缓存管理者,进行统一管理。
工作原理:
List<E> list = (List<E>) tcm.getObject(cache, key); //获取当前key在二级缓存是否对应数据 public Object getObject(Cache cache, CacheKey key) { return getTransactionalCache(cache).getObject(key); } //获取事务预缓存。 private TransactionalCache getTransactionalCache(Cache cache) { TransactionalCache txCache = transactionalCaches.get(cache); if (txCache == null) { txCache = new TransactionalCache(cache); transactionalCaches.put(cache, txCache); } return txCache; }
查询过程
- 通过TransactionalCacheManager 查询二级缓存中是否由当前key对应的缓存数据。
- TransactionalCacheManager 首先会检查当前当前二级缓存是否被事务缓存TransactionalCache装饰,如果没有装饰,就创建一个TransactionalCache装饰一下二级缓存。
- TransactionalCache#getObject(Key)方法会去二级缓存中查询。
缓存过程:
//TransactionalCacheManager public void commit() { for (TransactionalCache txCache : transactionalCaches.values()) { txCache.commit(); } } //TransactionalCache public void commit() { if (clearOnCommit) { delegate.clear(); } flushPendingEntries();//刷到二级缓存中 reset();//清空预缓存 }
- 通过TransactionalCacheManager#putObject 方法把从数据查询的结果放入TransactionalCache预缓存中。
- 当Sqlsession执行commit时,执行TransactionalCacheManager#commit方法,把当前预缓存中的数据正式提交到二级缓存中。并清空预缓存区。
小结:
二级缓存的工作原理: 一个缓存执行器 + 一个预缓存 + 二级缓存
二级缓存的刷新
insert、update、delete操作后都会引发二级缓存的刷新
public int update(MappedStatement ms, Object parameterObject) throws SQLException { flushCacheIfRequired(ms);//刷新二级缓存 return delegate.update(ms, parameterObject); } private void flushCacheIfRequired(MappedStatement ms) { Cache cache = ms.getCache(); if (cache != null && ms.isFlushCacheRequired()) { tcm.clear(cache);//清空二级缓存 } }
总结:
在二级缓存的设计上,MyBatis大量地运用了装饰者模式,如CachingExecutor, 以及各种Cache接口的装饰器。
- 二级缓存实现了Sqlsession之间的缓存数据共享,属于namespace级别
- 二级缓存具有丰富的缓存策略。
- 二级缓存可由多个装饰器,与基础缓存组合而成
- 二级缓存工作由 一个缓存装饰执行器CachingExecutor和 一个事务型预缓存TransactionalCache 完成。
如果本文任何错误,请批评指教,不胜感激 !
如果觉得文章不错,点个赞吧