二级缓存
概述
启用二级缓存需要进行三步配置
< 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 ( ) ;
}
}
总结