mybatis精講(六)--二級快取
阿新 • • 發佈:2019-12-30
目錄
- 簡介
- 配置
- 原始碼
- CachingExecutor
- 缺點
- 自定義二級快取
簡介
上一章節我們簡單瞭解了二級快取的配置。今天我們詳細分析下二級快取以及為什麼不建議使用二級快取。
一級快取針對的是sqlsession。二級快取針對的是namespace層面的。
配置
- 之前我們已經提到了配置二級快取以及配置自定義的二級快取。下面我們從頭開始實現二級快取。
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
這個屬性是控制二級快取的配置的。而這個屬性在Configuration中預設是true。這裡說明了mybatis預設是開啟快取功能的。二級快取和一級快取的區別其實除了範圍以外,他們的不同點就是順序不同。真正開啟二級快取的是在mapper的xml中配置cache標籤就行了。
- 我們這裡在StudentMapper.xml中配置.然後我們在test類中進行獲取兩次sqlsession呼叫同一個sql.
SqlSession sqlSession = SqlSessionFactoryUtils.openSqlsession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); Student student = mapper.getStudentByIdAndName("1", "1"); System.out.println(student); SqlSession sqlSession1 = SqlSessionFactoryUtils.sqlSessionFactory.openSession(); StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class); Student studentByIdAndName = mapper1.getStudentByIdAndName("1", "1"); System.out.println(studentByIdAndName);
- 但是結果確實很意外。事實上並沒有只調用一次sql。而是呼叫了兩次。這僅僅是結果上的異常。我們用的是Student這個結果接受的。我們再從程式碼層面上看看
@Data @Builder @Accessors(chain = true) public class Student { /** * 學生索引id */ private String id; /** * 姓名 */ private String userName; /** * 使用者暱稱 */ private String userNick; /** * 年齡 */ private Integer age; /** * 性別 true : 男 ; false : 女 */ private SexEnum sex; /** * 生日 */ private Date birth; /** * 身高 */ private Double height; }
- 細心的夥伴也許能夠發現。我們這個實體並沒有實現序列化。但是之前我們已經說過了二級快取的實體需要序列化。按道理來說應該報錯的。這就說明我們二級快取開啟,或者確切的說應該說是二級快取沒有起到作用。
- 那麼我們先將實體進行序列化。然後啟動發現並沒有任何效果。我們來看看
CacheingExecutor.commit()
這個方法裡面有事物的提交tcm.commit()
。
- 這個地方就是進行快取儲存的。我們再來看看mybatis是如何解析mapper.xml中配置的cache標籤的。
- 由上面程式碼我們得知mybatis會建立一個快取物件。裡面具體是通過一個build方法來建立的。我們在來看看build方法裡是啥東西。
- setStandardDecorators這個方法我們不知道做啥的。但是熟悉設計模式的都知道Decorator這個詞是裝飾者模式。這裡這個方法也是用來裝飾用的。看看mybatis為我們裝飾了那些東西。
- 首先在newBaseCacheInstance方法中建立原始物件PreprtualCache.然後是載入預設提供的回收機制用的Cache。這個實在build前設定的。
- 然後就是通過setStandardDecorators進行裝飾了。
所以他的裝飾鏈為:SynchronizedCache->LogginCache->SerializedCache->LruCache->PerPetualCache
而在上面的tcm.commit就是在SerializedCache進行快取物件的。所以我們之前的程式碼是sqlsession沒有提交。所以程式碼只要稍微改動下。
SqlSession sqlSession = SqlSessionFactoryUtils.openSqlsession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.getStudentByIdAndName("1", "1");
System.out.println(student);
sqlSession.commit();
SqlSession sqlSession1 = SqlSessionFactoryUtils.sqlSessionFactory.openSession();
StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
Student studentByIdAndName = mapper1.getStudentByIdAndName("1", "1");
System.out.println(studentByIdAndName);
SynchronizedCache : 同步Cache.這個類就是保證執行緒安全。所以他的方法基本上是加上
synchronized
來保證執行緒安全的。LoggingCache : 日誌。在上面我們有個日誌是Cache Hit Ratio 0.5 表示二級快取的命中率。
SerializedCache : 就是用來序列化資料的。
LruCache : 回收cache的演算法
PerPetualCache :基本Cache .
原始碼
CachingExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//獲取Cache物件
Cache cache = ms.getCache();
if (cache != null) {
//根據statment配置重新整理快取,預設是insert、update、delete會重新整理快取
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);
}
return list;
}
}
//沒有開啟二級快取則繼續往下走
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
缺點
- 二級快取因為更加廣泛,所以容易造成髒資料。尤其是在關聯查詢的時候有序無法控制重新整理力度。很容易出現髒讀。
自定義二級快取
- 在之前我們瞭解到的
PerpetualCache
是快取鏈上最基本的快取類。我們自定義的快取就是替代這個類的。在mybatis中會現根據我們註冊進來的類進行例項化。如果沒有則用預設的PerpetualCache
這個類作為基礎快取類。