MyBatis 學習記錄5 MyBatis的二級緩存

 

主題

  以前學習了一下MyBatis的一級緩存,主要涉及到BaseExecutor這個類. 如今準備學習記錄下MyBatis二級緩存.java

 

配置二級緩存與初始化發生的事情

首先二級緩存默認是不開啓的,須要本身配置開啓.mysql

如上圖,須要在configuration裏去開啓.sql

其次在須要用到二級緩存的Mapper的配置裏作一些操做,以下圖,增長一個cache節點數據庫

至此就能夠在UserMapper上開啓二級緩存了.apache

 

當MaBatis初始化的時候,須要解析各類XML配置來生成SQLSessionFactory,解析Mapper的配置也是其中一環.而解析Mapper的配置中,解析Cache節點又是其中的一部分.緩存

 

這個解析cache會作什麼事情呢?app

如上圖所示,會往builderAssistant裏去新建一個Cache對象(其中的參數是你以前XML裏配置的).ide

Cache類的設計也是裝飾着模式,層層嵌套.好比咱們new出來的Cache中會包裹一個打印緩存命中信息的LoggingCache,咱們指定的LruCache包裹着PerpetualCache(一級緩存也使用這個Cache)等等.學習

 

MapperBuilderAssistant是個Builder模式,解析Mapper的最後會調用assistant的addMappedStatement方法.fetch

 1     public MappedStatement addMappedStatement(
 2             String id,
 3             SqlSource sqlSource,
 4             StatementType statementType,
 5             SqlCommandType sqlCommandType,
 6             Integer fetchSize,
 7             Integer timeout,
 8             String parameterMap,
 9             Class<?> parameterType,
10             String resultMap,
11             Class<?> resultType,
12             ResultSetType resultSetType,
13             boolean flushCache,
14             boolean useCache,
15             boolean resultOrdered,
16             KeyGenerator keyGenerator,
17             String keyProperty,
18             String keyColumn,
19             String databaseId,
20             LanguageDriver lang,
21             String resultSets) {
22 
23         if (unresolvedCacheRef) {
24             throw new IncompleteElementException("Cache-ref not yet resolved");
25         }
26 
27         id = applyCurrentNamespace(id, false);
28         boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
29 
30         MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
31                 .resource(resource)
32                 .fetchSize(fetchSize)
33                 .timeout(timeout)
34                 .statementType(statementType)
35                 .keyGenerator(keyGenerator)
36                 .keyProperty(keyProperty)
37                 .keyColumn(keyColumn)
38                 .databaseId(databaseId)
39                 .lang(lang)
40                 .resultOrdered(resultOrdered)
41                 .resultSets(resultSets)
42                 .resultMaps(getStatementResultMaps(resultMap, resultType, id))
43                 .resultSetType(resultSetType)
44                 .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
45                 .useCache(valueOrDefault(useCache, isSelect))
46                 .cache(currentCache);
47 
48         ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
49         if (statementParameterMap != null) {
50             statementBuilder.parameterMap(statementParameterMap);
51         }
52 
53         MappedStatement statement = statementBuilder.build();
54         configuration.addMappedStatement(statement);
55         return statement;
56     }
View Code

把剛纔new出來的cache設置到MappedStatement.Builder中,最後build出一個MappedStatement,並添加到Configuration裏. 以下圖

 

因此其實咱們剛纔在Mapper的XML裏配置的cache,最後生成對應的java Cache對象,是放在MappedStatement裏的,而MappedStatement是什麼東西? 看了以下3個截圖你們應該就明白了.就是你Mapper XML裏配置的一個一個DML對應的節點,固然1個Mapper.XML裏會有不少這樣的節點,而他們是公用1個Cache的.

MappedStatement是全局惟一的對象,被放到了Configuration裏,不一樣的Executor須要用到的時候都會從Configuration裏去找SQL對應的MappedStatement.(實際上是SqlSession調用Executor的query方法的時候會去根據類.方法名在configuration裏查找mappedStatement並傳入)

 

 

 

 

CachingExecutor

正如以前文章記錄的那樣,CachingExecutor是包裹在BaseExecutor外的一層Executor,使用的是裝飾着模式. 二級緩存主要是在它裏面實現的.

先來看一個Demo

2018-10-14 12:04:17,138 DEBUG [main] logging.LogFactory : Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [test.mapper.UserMapper]: 0.0
Opening JDBC Connection
Sun Oct 14 12:04:17 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Created connection 25300561.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1820e51]
==>  Preparing: select id, user_id, user_name, real_name, email, creator_uid, modifier_uid, created_at, updated_at, del from user where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, user_id, user_name, real_name, email, creator_uid, modifier_uid, created_at, updated_at, del
<==        Row: 1, 1, test, realName, jyzjyz12@163.com, 1, 1, 2018-09-24 10:10:43.0, 2018-09-24 10:10:46.0, 0
<==      Total: 1
User{id=1, userId=1, userName='test', realName='realName', email='jyzjyz12@163.com', creatorUid=1, modifierUid=1, createdAt=Mon Sep 24 10:10:43 CST 2018, updatedAt=Mon Sep 24 10:10:46 CST 2018, del=false}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1820e51]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1820e51]
Returned connection 25300561 to pool.
Cache Hit Ratio [test.mapper.UserMapper]: 0.5
User{id=1, userId=1, userName='test', realName='realName', email='jyzjyz12@163.com', creatorUid=1, modifierUid=1, createdAt=Mon Sep 24 10:10:43 CST 2018, updatedAt=Mon Sep 24 10:10:46 CST 2018, del=false}

如上圖,從1個SQLSessionFactory裏獲取了2個SqlSession,分別調用了1次selectByPrimaryKey.

而紅色部分輸出看到 第一個sqlSession打印了1次SQL, 第二次調用selectByPrimaryKey的時候會打印藍色的部分,輸出緩存命中,機率爲1/2,而後沒有打印紅色的查詢語句,而是直接輸出返回打印了以前緩存中的對象.

 

CachingExecutor的query方法以下圖

與BaseExecutor的處理方法大體相似,只是先會從MapperedStatement裏去尋找是否配置過緩存,有的話就從TransactionalCacheManager中經過Cache作爲Key去找TransactionalCache,再從TransactionalCache中根據CacheKey查找結果, TransactionalCache也是Cache的一種實現,目的是爲了支持事務回滾提交操做.

TransactionalCache中緩存對象的key爲CacheKey,CacheKey以前在一級緩存中已經分享過,value爲數據庫查詢結果,即一個ArrayList.

若是緩存中沒有,那就委託內部wrapped的對象,也就是SImpleExecutor去調用query去數據庫中找.

 

小結

總結一下二級緩存如何實現:

1.初始化階段初始化Cache設置到MappedStatement裏,MappedStatement設置到Configuration裏.

2.執行查詢的時候SqlSession去Configuration裏找到MappedStatement傳給CachingExecutor.

3.CachingExecutor獲取MappedStatement再獲取其中的Cache,根據Cache是否null,判斷是否有開啓二級緩存.

4.若是有開啓,那Cache不爲null,去TransactionalCacheManager中根據Cache爲Key找到TransactionalCache,爲null就委託SImpleExecutor繼續查數據庫.不爲null,就在TransactionalCache根據CacheKey找到對應的Value並返回.

相關文章
相關標籤/搜索