MyBatis二级缓存在默认情况下是关闭的,因此需要通过设置cacheEnabled
参数值为true来开启二级缓存
SqlSession 将 执行Mapper的逻辑 委托给 Executor组件完成,而Executor接口有几种不同的实现,分别为SimpleExecutor、BatchExecutor、ReuseExecutor
另外,还有一个比较特殊的CachingExecutor,CachingExecutor用到了装饰器模式,在其他几种Executor的基础上增加了二级缓存功能
Executor实例 采用 工厂模式 创建,Configuration类 提供了一个 工厂方法newExecutor(),该方法返回一个Executor对象,如下所示:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
如果cacheEnabled属性值为true(开启了二级缓存),则使用CachingExecutor对普通的Executor对象进行装饰,CachingExecutor在普通Executor的基础上增加了二级缓存功能
private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager(); public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); }
CachingExecutor类中维护了一个TransactionalCacheManager实例,用于管理所有的二级缓存对象
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); // 先创建CacheKey CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } @Override 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, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { // 执行查询 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 查询数据库之后添加到缓存 tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } // 如果缓存为空,查询数据库 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
在CachingExecutor的query()方法中,首先调用createCacheKey()方法创建缓存Key对象
然后调用MappedStatement对象的getCache()方法 获取 MappedStatement对象中 维护的 二级缓存对象
然后尝试从 二级缓存对象中 获取结果,如果获取不到,则调用 目标Executor对象的query()方法 从数据库获取数据,再将数据添加到二级缓存中
当执行更新语句后,同一命名空间下的二级缓存将会被清空
@Override 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(); // 如果配置了<cache/>节点或<cache-ref/>并且设置了flushCache为true if (cache != null && ms.isFlushCacheRequired()) { // 刷新二级缓存 tcm.clear(cache); } }
在flushCacheIfRequired()方法中会判断<select|update|delete|insert>标签的flushCache属性,如果属性值为true,就清空缓存
<select>标签的flushCache属性值默认为false
<update|delete|insert>标签的flushCache属性值默认为true
最终执行的是TransactionalCacheManager#clear方法,传入的是 MappedStatement 的 缓存属性变量(Cache类型变量)
XMLMapperBuilder在解析Mapper配置时会调用cacheElement()方法解析<cache>标签
private void cacheElement(XNode context) { if (context != null) { String type = context.getStringAttribute("type", "PERPETUAL"); Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); String eviction = context.getStringAttribute("eviction", "LRU"); Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); Long flushInterval = context.getLongAttribute("flushInterval"); Integer size = context.getIntAttribute("size"); boolean readWrite = !context.getBooleanAttribute("readOnly", false); boolean blocking = context.getBooleanAttribute("blocking", false); Properties props = context.getChildrenAsProperties(); builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } } public Cache useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props) { Cache cache = new CacheBuilder(currentNamespace) .implementation(valueOrDefault(typeClass, PerpetualCache.class)) .addDecorator(valueOrDefault(evictionClass, LruCache.class)) .clearInterval(flushInterval) .size(size) .readWrite(readWrite) .blocking(blocking) .properties(props) .build(); configuration.addCache(cache); currentCache = cache; return cache; }
在获取<cache>标签的所有属性信息后,调用MapperBuilderAssistant对象的userNewCache()
方法创建二级缓存实例
然后通过MapperBuilderAssistant的currentCache属性 保存 二级缓存对象的引用
在调用MapperBuilderAssistant对象的addMappedStatement()方法(在XMLStatementBuilder#parseStatementNode方法中) 创建 MappedStatement对象时 会将 当前命名空间 对应的 二级缓存对象的引用 添加到 MappedStatement对象中
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); // 这里
每个会话中持有一个CachingExecutor(缓存执行器)
所以每个会话都有自己单独的事务缓存管理器
public class TransactionalCacheManager { private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>(); .... }
在TransactionalCacheManager类中,通过一个HashMap对象(暂存区集合) 维护 所有 二级缓存实例 对应的 TransactionalCache(事务缓存)对象
key是Cache实现类实例对象
在TransactionalCacheManager类的getObject()方法和putObject()方法中 都会调用 getTransactionalCache()方法 获取 二级缓存对象 对应的 TransactionalCache对象,然后 对TransactionalCache对象 进行操作
在getTransactionalCache()方法中,首先从HashMap对象中 获取 二级缓存对象 对应的 TransactionalCache对象
如果获取不到,则 创建新的TransactionalCache对象 添加到 HashMap对象中
public void clear(Cache cache) { getTransactionalCache(cache).clear(); } private TransactionalCache getTransactionalCache(Cache cache) { return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new); }
根据 MappedStatement 的 缓存对象(Cache类型变量) 通过getTransactionalCache方法 在transactionalCaches集合中 查询 是否有对应的TransactionalCache对象
执行的是TransactionalCache#clear方法
public void putObject(Cache cache, CacheKey key, Object value) { getTransactionalCache(cache).putObject(key, value); }
先根据 MappedStatement 的 缓存对象(Cache类型变量) 在transactionalCaches集合中 查询 是否有对应的 TransactionalCache对象
如果没有new一个TransactionalCache对象
然后向 TransactionalCache对象 中 放入CacheKey 和 查询结果
执行的是TransactionalCache#putObject方法
public Object getObject(Cache cache, CacheKey key) { return getTransactionalCache(cache).getObject(key); }
传入的参数 是 MappedStatement 的 缓存对象(Cache类型变量) 和 CacheKey
执行的是TransactionalCache#getObject方法
public class TransactionalCache implements Cache { private static final Log log = LogFactory.getLog(TransactionalCache.class); // 装饰模式 private final Cache delegate; // 提交的时候 清除cache 的 标志位 private boolean clearOnCommit; // 待提交的集合 private final Map<Object, Object> entriesToAddOnCommit; // 未命中的缓存 private final Set<Object> entriesMissedInCache; public TransactionalCache(Cache delegate) { this.delegate = delegate; this.clearOnCommit = false; this.entriesToAddOnCommit = new HashMap<>(); this.entriesMissedInCache = new HashSet<>(); } //... }
@Override public void clear() { clearOnCommit = true; entriesToAddOnCommit.clear(); }
@Override public void putObject(Object key, Object object) { entriesToAddOnCommit.put(key, object); }
向 待提交集合中 放入CacheKey 和 查询结果
@Override public Object getObject(Object key) { // issue #116 Object object = delegate.getObject(key); if (object == null) { entriesMissedInCache.add(key); } // issue #146 if (clearOnCommit) { return null; } else { return object; } }
根据key从delegate(Cache实现类的变量实例)查抄,如果查询到为空,放入entriesMissedInCache-未命中集合
问题:putObject是放入entriesToAddOnCommit集合,getObject是从delegate(Cache实现类的变量实例)的cache集合中获取,那从entriesToAddOnCommit到delegate是在什么时候?
在事务commit的时候
public void commit() { if (clearOnCommit) { delegate.clear(); } flushPendingEntries(); reset(); } private void flushPendingEntries() { // 循环entriesToAddOnCommit,放入delegate for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) { delegate.putObject(entry.getKey(), entry.getValue()); } // 循环entriesMissedInCache,如果在entriesToAddOnCommit不存在,也放入delegate,缓存的查询结果为空 for (Object entry : entriesMissedInCache) { if (!entriesToAddOnCommit.containsKey(entry)) { delegate.putObject(entry, null); } } }