二级缓存
概述
启用二级缓存需要进行三步配置
< settings> < setting name = " cacheEnabled" value = " true" /> </ settings>
在需要使用二级缓存的 Mapper 配置文件中配置标签
< cache> </ cache>
在具体 CURD 标签上配置 useCache=true
< select id = " findByCondition" resultType = " com.itheima.pojo.User" useCache = " true" > SELECT id, name FROM user WHERE id = #{id}</ select>
新增二级缓存测试,测试的 case 先是创建一个会话,执行一次查询,然后其中一个会话进行一次 commit() (否则没办法生效二级缓存),然后再使用另外一个会话查询一次
public class CacheTest { @Test public void secondLevelCacheTest ( ) throws IOException { InputStream resourceAsStream = Resources . getResourceAsStream ( "sqlMapConfig.xml" ) ; SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ( ) . build ( resourceAsStream) ; SqlSession sqlSession1 = sqlSessionFactory. openSession ( ) ; SqlSession sqlSession2 = sqlSessionFactory. openSession ( ) ; User user1 = sqlSession1. selectOne ( "com.itheima.mapper.UserMapper.findByCondition" , 1 ) ; sqlSession1. commit ( ) ; User user2 = sqlSession2. selectOne ( "com.itheima.mapper.UserMapper.findByCondition" , 1 ) ; System . out. println ( user1== user2) ; System . out. println ( user1) ; System . out. println ( user2) ; sqlSession1. close ( ) ; }
}
标签 <cache> 解析
问题
首先继续回到解析配置文件部分
public class CacheTest { @Test public void secondLevelCacheTest ( ) throws IOException { InputStream resourceAsStream = Resources . getResourceAsStream ( "sqlMapConfig.xml" ) ; SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ( ) . build ( resourceAsStream) ; . . . }
}
SqlSessionFactoryBuilder 创建 XMLConfigBuilder 解析配置文件,然后开始分析
public class SqlSessionFactoryBuilder { . . . public SqlSessionFactory build ( InputStream inputStream) { return build ( inputStream, null , null ) ; } public SqlSessionFactory build ( InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder ( inputStream, environment, properties) ; return build ( parser. parse ( ) ) ; } catch ( Exception e) { throw ExceptionFactory . wrapException ( "Error building SqlSession." , e) ; } finally { ErrorContext . instance ( ) . reset ( ) ; try { inputStream. close ( ) ; } catch ( IOException e) { } } }
}
XMLConfigBuilder 开始从根节点 /configuration 开始解析,因为关注的是二级缓存,我们从解析 /mappers 开始
public class XMLConfigBuilder extends BaseBuilder { private final XPathParser parser; . . . public Configuration parse ( ) { if ( parsed) { throw new BuilderException ( "Each XMLConfigBuilder can only be used once." ) ; } parsed = true ; parseConfiguration ( parser. evalNode ( "/configuration" ) ) ; return configuration; } private void parseConfiguration ( XNode root) { try { . . . mapperElement ( root. evalNode ( "mappers" ) ) ; } catch ( Exception e) { throw new BuilderException ( "Error parsing SQL Mapper Configuration. Cause: " + e, e) ; } }
}
mapperElement() 在解析 /mapper 节点时,不管是解析的是要通过 /package 节点还是本身 /mapper 解析,最终会调用 XMLMapperBuilder 来解析 mapper 映射文件
public class XMLConfigBuilder extends BaseBuilder { protected final Configuration configuration; . . . private void mapperElement ( XNode parent) throws Exception { if ( parent != null ) { for ( XNode child : parent. getChildren ( ) ) { if ( "package" . equals ( child. getName ( ) ) ) { String mapperPackage = child. getStringAttribute ( "name" ) ; configuration. addMappers ( mapperPackage) ; } else { . . . . } } } }
}
configuration.addMappers() 最终就交由 XMLMapperBuilder 来解析 mapper 映射文件
public class XMLMapperBuilder extends BaseBuilder { protected final Configuration configuration; private final XPathParser parser; . . . public void parse ( ) { if ( ! configuration. isResourceLoaded ( resource) ) { configurationElement ( parser. evalNode ( "/mapper" ) ) ; . . . } . . . }
}
当 XMLMapperBuilder 解析 mapper.xml 时,就会解析对应的 <cache> 子标签,这个是开启二级缓存的标志
public class XMLMapperBuilder extends BaseBuilder { . . . private void configurationElement ( XNode context) { try { . . . cacheElement ( context. evalNode ( "cache" ) ) ; . . . } catch ( Exception e) { throw new BuilderException ( "Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e) ; } }
}
解析 /cache 节点时,首先检查是否已经指定 type 属性,如果有,则创建指定 type 的缓存对象,否则默认的二级缓存,创建的是 PerpetualCache ,然后根据二级缓存的所有配置,创建对应的属性对象,通过 builderAssistant.useNewCache() 创建二级缓存
public class XMLMapperBuilder extends BaseBuilder { . . . private final MapperBuilderAssistant builderAssistant; protected final TypeAliasRegistry typeAliasRegistry; 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) ; } }
}
MapperBuilderAssistant 就会将所有 /cache 属性组装生成 cache 对象,然后添加到 configuration 对象中(其中内部会获取 cache 对象的 id:com.itheima.mapper.UserMapper 保存到 Map 中),然后把缓存赋值给 currentCache 属性,等会还得使用
public class MapperBuilderAssistant extends BaseBuilder { private Cache currentCache; . . . 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 节点后,还需要解析 /select|/insert|/update|/delete 节点,因为这里会把刚刚创建的二级缓存也放到 MappedStatement 中
public class XMLMapperBuilder extends BaseBuilder { . . . private void configurationElement ( XNode context) { try { . . . buildStatementFromContext ( context. evalNodes ( "select|insert|update|delete" ) ) ; } catch ( Exception e) { throw new BuilderException ( "Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e) ; } }
}
下面解析 /select|/insert|/update|/delete 节点前,会分别创建 XMLStatementBuilder 来解析并创建 MappedStatement 对象
public class XMLMapperBuilder extends BaseBuilder { protected final Configuration configuration; . . . private void buildStatementFromContext ( List < XNode > list) { if ( configuration. getDatabaseId ( ) != null ) { buildStatementFromContext ( list, configuration. getDatabaseId ( ) ) ; } buildStatementFromContext ( list, null ) ; } private void buildStatementFromContext ( List < XNode > list, String requiredDatabaseId) { for ( XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder ( configuration, builderAssistant, context, requiredDatabaseId) ; try { statementParser. parseStatementNode ( ) ; } catch ( IncompleteElementException e) { configuration. addIncompleteStatement ( statementParser) ; } } }
}
statementParser.parseStatementNode() 解析节点时,先判断是否标记了 useCache 属性,然后通过构建者助手,创建 MappedStatement 对象
public class XMLStatementBuilder extends BaseBuilder { private final MapperBuilderAssistant builderAssistant; private final XNode context; . . . public void parseStatementNode ( ) { . . . boolean useCache = context. getBooleanAttribute ( "useCache" , isSelect) ; . . . builderAssistant. addMappedStatement ( id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets) ; }
}
然后 MapperBuilderAssistant 当时是把二级缓存放到了 currentCache 属性中,为的就是把创建出来的缓存存入 MappedStatement 中,创建 MappedStatement 后,存入 Configuration 对象,最终返回构建好的 DefaultSqlSessionFactory 。从上面流程可以看出,二级缓存,其实是配置文件命名空间下共享的
public class MapperBuilderAssistant extends BaseBuilder { private Cache currentCache; protected final Configuration configuration; . . . public MappedStatement addMappedStatement ( String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class < ? > parameterType, String resultMap, Class < ? > resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) { if ( unresolvedCacheRef) { throw new IncompleteElementException ( "Cache-ref not yet resolved" ) ; } id = applyCurrentNamespace ( id, false ) ; boolean isSelect = sqlCommandType == SqlCommandType . SELECT; 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) ; ParameterMap statementParameterMap = getStatementParameterMap ( parameterMap, parameterType, id) ; if ( statementParameterMap != null ) { statementBuilder. parameterMap ( statementParameterMap) ; } MappedStatement statement = statementBuilder. build ( ) ; configuration. addMappedStatement ( statement) ; return statement; }
}
总结
二级缓存执行
问题
同时开启一级缓存,二级缓存。优先级? 为什么只有执行 sqlSession.commit() 或者 sqlSession.close() 二级缓存才会生效
继续沿用上一次的测试 case 来研究解决这些问题
public class CacheTest { @Test public void secondLevelCacheTest ( ) throws IOException { InputStream resourceAsStream = Resources . getResourceAsStream ( "sqlMapConfig.xml" ) ; SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ( ) . build ( resourceAsStream) ; SqlSession sqlSession1 = sqlSessionFactory. openSession ( ) ; SqlSession sqlSession2 = sqlSessionFactory. openSession ( ) ; User user1 = sqlSession1. selectOne ( "com.itheima.mapper.UserMapper.findByCondition" , 1 ) ; sqlSession1. commit ( ) ; User user2 = sqlSession2. selectOne ( "com.itheima.mapper.UserMapper.findByCondition" , 1 ) ; System . out. println ( user1== user2) ; System . out. println ( user1) ; System . out. println ( user2) ; sqlSession1. close ( ) ; }
}
首先第一次执行 selectOne() 继续经过多次 selectList() 方法,生成 MappedStatement 然后委派给 Executor 执行
public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; . . . private < E > List < E > selectList ( String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration. getMappedStatement ( statement) ; return executor. query ( ms, wrapCollection ( parameter) , rowBounds, handler) ; } catch ( Exception e) { throw ExceptionFactory . wrapException ( "Error querying database. Cause: " + e, e) ; } finally { ErrorContext . instance ( ) . reset ( ) ; } }
}
CachingExecutor 执行查询时,会先生成缓存键 CacheKey ,然后从 MappedStatement 中获取二级缓存,如果 MappedStatement 对应的 sql 语句配置了 flushCache=true
,那么就会在执行前刷新缓存。这里的 case 并没有配置,所以继续执行。尝试从二级缓存中获取数据
public class CachingExecutor implements Executor { private final TransactionalCacheManager tcm = new TransactionalCacheManager ( ) ; . . . @Override public < E > List < E > query ( MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms. getBoundSql ( parameterObject) ; 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) ; . . . return list; } } return delegate. query ( ms, parameterObject, rowBounds, resultHandler, key, boundSql) ; }
}
这里实际是由事务缓存管理器 TransactionalCacheManager 来获取缓存数据,它只有一个关键属性 transactionalCaches ,其中 key 是二级缓存,而 TransactionalCache 是对应的一个临时事务缓存,一开始时 TransactionalCache 还没创建,所以会先创建,然后从 TransactionalCache 获取数据。后面才明白 TransactionalCache 的作用
public class TransactionalCacheManager { private final Map < Cache , TransactionalCache > transactionalCaches = new HashMap < > ( ) ; . . . public Object getObject ( Cache cache, CacheKey key) { return getTransactionalCache ( cache) . getObject ( key) ; } private TransactionalCache getTransactionalCache ( Cache cache) { return MapUtil . computeIfAbsent ( transactionalCaches, cache, TransactionalCache :: new ) ; }
}
实际 TransactionalCache 包装了二级缓存,所以在执行 getObject() 方法时,会先从二级缓存中拿到数据,如果拿不到,则存入一个 entriesMissedInCache 的 Set 集合中,返回返回一个空对象
public class TransactionalCache implements Cache { private final Cache delegate; private final Set < Object > entriesMissedInCache; @Override public Object getObject ( Object key) { Object object = delegate. getObject ( key) ; if ( object == null ) { entriesMissedInCache. add ( key) ; } if ( clearOnCommit) { return null ; } else { return object; } }
}
第一次执行 selectOne() 肯定是没有缓存结果的,所以 CachingExecutor 还是会委派给 SimpleExecutor 执行查询
public class CachingExecutor implements Executor { private final Executor delegate; . . . @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 ) { . . . if ( list == null ) { list = delegate. query ( ms, parameterObject, rowBounds, resultHandler, key, boundSql) ; . . . } return list; } } return delegate. query ( ms, parameterObject, rowBounds, resultHandler, key, boundSql) ; }
}
SimpleExecutor 执行查询则首先通过一级缓存获取数据,如果没有数据,就从数据查询,查询后将数据存入一级缓存,然后返回数据。从这里得知第一个问题答案,明显是二级缓存最优先
public abstract class BaseExecutor implements Executor { protected PerpetualCache localCache; . . . @Override public < E > List < E > query ( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext . instance ( ) . resource ( ms. getResource ( ) ) . activity ( "executing a query" ) . object ( ms. getId ( ) ) ; if ( closed) { throw new ExecutorException ( "Executor was closed." ) ; } if ( queryStack == 0 && ms. isFlushCacheRequired ( ) ) { clearLocalCache ( ) ; } List < E > list; try { queryStack++ ; list = resultHandler == null ? ( List < E > ) localCache. getObject ( key) : null ; if ( list != null ) { handleLocallyCachedOutputParameters ( ms, key, parameter, boundSql) ; } else { list = queryFromDatabase ( ms, parameter, rowBounds, resultHandler, key, boundSql) ; } } finally { queryStack-- ; } . . . return list; } private < E > List < E > queryFromDatabase ( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List < E > list; localCache. putObject ( key, EXECUTION_PLACEHOLDER) ; try { list = doQuery ( ms, parameter, rowBounds, resultHandler, boundSql) ; } finally { localCache. removeObject ( key) ; } localCache. putObject ( key, list) ; if ( ms. getStatementType ( ) == StatementType . CALLABLE) { localOutputParameterCache. putObject ( key, parameter) ; } return list; }
}
当获取到结果得时候,就要存到二级缓存中,这时候并不是真的存,下面会继续解释
public class CachingExecutor implements Executor { private final Executor delegate; . . . @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 ) { . . . if ( list == null ) { list = delegate. query ( ms, parameterObject, rowBounds, resultHandler, key, boundSql) ; tcm. putObject ( cache, key, list) ; } return list; } } return delegate. query ( ms, parameterObject, rowBounds, resultHandler, key, boundSql) ; }
}
TransactionalCacheManager 通过 cache 获取对应的 TransactionalCache ,如果没有则新建,然后将结果存入缓存
public class TransactionalCacheManager { private final Map < Cache , TransactionalCache > transactionalCaches = new HashMap < > ( ) ; . . . public void putObject ( Cache cache, CacheKey key, Object value) { getTransactionalCache ( cache) . putObject ( key, value) ; } private TransactionalCache getTransactionalCache ( Cache cache) { return MapUtil . computeIfAbsent ( transactionalCaches, cache, TransactionalCache :: new ) ; }
}
实际 TransactionalCache 存入时,只会存入到 entriesToAddOnCommit 这个 Map 缓存中,并非真实的缓存对象 delegate 中
public class TransactionalCache implements Cache { private final Map < Object , Object > entriesToAddOnCommit; . . . @Override public void putObject ( Object key, Object object) { entriesToAddOnCommit. put ( key, object) ; }
}
查询到数据后,必须执行 commit() 操作,二级缓存才会真正生效,下面来看看 commit() 方法是什么时候把数据放入二级缓存
public class CacheTest { @Test public void secondLevelCacheTest ( ) throws IOException { . . . sqlSession1. commit ( ) ; . . . }
}
commit() 操作的提交,也是交由 CachingExecutor 来执行
public class DefaultSqlSession implements SqlSession { private final Executor executor; . . . @Override public void commit ( ) { commit ( false ) ; } @Override public void commit ( boolean force) { try { executor. commit ( isCommitOrRollbackRequired ( force) ) ; dirty = false ; } catch ( Exception e) { throw ExceptionFactory . wrapException ( "Error committing transaction. Cause: " + e, e) ; } finally { ErrorContext . instance ( ) . reset ( ) ; } }
}
除了 CachingExecutor 委派 SimpleExecutor 执行真正的 commit() ,与二级缓存相关最值得关注的是 TransactionalCacheManager 的 commit() 操作
public class CachingExecutor implements Executor { private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager ( ) ; . . . @Override public void commit ( boolean required) throws SQLException { delegate. commit ( required) ; tcm. commit ( ) ; }
}
TransactionalCacheManager 执行 commit() 时,会遍历 transactionalCaches 也来执行 commit() 操作
public class TransactionalCacheManager { private final Map < Cache , TransactionalCache > transactionalCaches = new HashMap < > ( ) ; . . . public void commit ( ) { for ( TransactionalCache txCache : transactionalCaches. values ( ) ) { txCache. commit ( ) ; } }
}
TransactionalCache 的 commit() ,会将 entriesToAddOnCommit 、entriesMissedInCache 真正刷入二级缓存的 PertualCache 中。首先遍历 entriesToAddOnCommit 把在 Map 中输入插入真正的缓存后,再将 entriesMissedInCache 不在 entriesToAddOnCommit 中的数据也刷入二级缓存中,只不过对应 key 的值为 null
public class TransactionalCache implements Cache { private final Cache delegate; private boolean clearOnCommit; private final Map < Object , Object > entriesToAddOnCommit; private final Set < Object > entriesMissedInCache; . . . public void commit ( ) { if ( clearOnCommit) { delegate. clear ( ) ; } flushPendingEntries ( ) ; reset ( ) ; } private void flushPendingEntries ( ) { for ( Map. Entry < Object , Object > entry : entriesToAddOnCommit. entrySet ( ) ) { delegate. putObject ( entry. getKey ( ) , entry. getValue ( ) ) ; } for ( Object entry : entriesMissedInCache) { if ( ! entriesToAddOnCommit. containsKey ( entry) ) { delegate. putObject ( entry, null ) ; } } }
}
完成 commit() 后,执行第二次 selectOne() 时,因为数据已经在二级缓存中,所以就能查找到数据,这就解答了第 2 个问题,为什么要在 commit() 时拿到数据,因为第一次存的时候只是放在个临时 Map 中,没真正存到二级缓存 总结
需要 commit() 才会放入二级缓存 流程总结
更新方法不会清空二级缓存
问题:
首先在测试 case 第一次 selectOne() 并 commit() 后面增加一个 update() 操作
public class CacheTest { @Test public void secondLevelCacheTest ( ) throws IOException { . . . User user1 = sqlSession1. selectOne ( "com.itheima.mapper.UserMapper.findByCondition" , 1 ) ; sqlSession1. commit ( ) ; SqlSession sqlSession3 = sqlSessionFactory. openSession ( ) ; User user = new User ( ) ; user. setId ( 1L ) ; user. setName ( "tom" ) ; sqlSession3. update ( "com.itheima.mapper.UserMapper.updateUser" , user) ; sqlSession3. commit ( ) ; . . . }
}
执行 update() 时,SqlSession 创建 MappedStatement 后,实际还是交由 CachingExecutor 来执行
public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; . . . @Override public int update ( String statement, Object parameter) { try { dirty = true ; MappedStatement ms = configuration. getMappedStatement ( statement) ; return executor. update ( ms, wrapCollection ( parameter) ) ; } catch ( Exception e) { throw ExceptionFactory . wrapException ( "Error updating database. Cause: " + e, e) ; } finally { ErrorContext . instance ( ) . reset ( ) ; } }
}
CachingExecutor 在 update() 时,进行二级缓存的清空,也就是委派 TransactionalCacheManager 进行 clear() 操作
public class CachingExecutor implements Executor { private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager ( ) ; . . . @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 ( ) ; if ( cache != null && ms. isFlushCacheRequired ( ) ) { tcm. clear ( cache) ; } }
}
TransactionalCacheManager 根据原生 Cache ,拿到 TransactionalCache 执行 clear() 操作
public class TransactionalCacheManager { private final Map < Cache , TransactionalCache > transactionalCaches = new HashMap < > ( ) ; . . . public void clear ( Cache cache) { getTransactionalCache ( cache) . clear ( ) ; } private TransactionalCache getTransactionalCache ( Cache cache) { return MapUtil . computeIfAbsent ( transactionalCaches, cache, TransactionalCache :: new ) ; }
}
update() 方法的二级缓存清理实际只是把属性 clearOnCommit 标记为 true,同时把 entriesToAddOnCommit 这个 Map 清空了,但是由于第一次 selectOne() 的时候,已经 commit() 了,所以落到了真正的二级缓存中,只清空了个临时事务缓存
public class TransactionalCache implements Cache { private boolean clearOnCommit; private final Map < Object , Object > entriesToAddOnCommit; . . . @Override public void clear ( ) { clearOnCommit = true ; entriesToAddOnCommit. clear ( ) ; }
}
update() 方法执行完成后,开始执行 commit() 方法,SqlSession 再次委派 CachingExecutor 执行 commit() 方法
public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; . . . @Override public void commit ( ) { commit ( false ) ; } @Override public void commit ( boolean force) { try { executor. commit ( isCommitOrRollbackRequired ( force) ) ; dirty = false ; } catch ( Exception e) { throw ExceptionFactory . wrapException ( "Error committing transaction. Cause: " + e, e) ; } finally { ErrorContext . instance ( ) . reset ( ) ; } }
}
CachingExecutor 委派 SimpleExecutor 执行完 commit() 方法后,再调用 TransactionalCacheManager 执行 commit() 方法
public class CachingExecutor implements Executor { private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager ( ) ; . . . @Override public void commit ( boolean required) throws SQLException { delegate. commit ( required) ; tcm. commit ( ) ; }
}
TransactionalCacheManager 遍历 transactionalCaches ,执行 commit() 方法
public class TransactionalCacheManager { private final Map < Cache , TransactionalCache > transactionalCaches = new HashMap < > ( ) ; . . . public void commit ( ) { for ( TransactionalCache txCache : transactionalCaches. values ( ) ) { txCache. commit ( ) ; } }
}
由于 update() 已经把 clearOnCommit 属性标记为 true,这时候,就真的把对应的二级缓存都清空了
public class TransactionalCache implements Cache { private final Cache delegate; . . . public void commit ( ) { if ( clearOnCommit) { delegate. clear ( ) ; } flushPendingEntries ( ) ; reset ( ) ; }
}
总结