mybatis原始碼詳細解析(2)---- 一級,二級快取
Mybatis快取的作用
每當我們使用 MyBatis 開啟一次和資料庫的會話,MyBatis 會建立出一個 SqlSession 物件表示一次資料庫會話。
在對資料庫的一次會話中,我們有可能會反覆地執行完全相同的查詢語句,如果不採取一些措施的話,每一次查詢都會查詢一次資料庫,而我們在極短的時間內做了完全相同的查詢,那麼它們的結果極有可能完全相同,由於查詢一次資料庫的代價很大,這有可能造成很大的資源浪費。
為了解決這一問題,減少資源的浪費,MyBatis會在表示會話的SqlSession物件中建立一個簡單的快取,將每次查詢到的結果結果快取起來,當下次查詢的時候,如果判斷先前有個完全一樣的查詢,會直接從快取中直接將結果取出,返回給使用者,不需要再進行一次資料庫查詢了。
當初始化執行器的時候會預設建立快取器(CachingExecutor):
上一篇的程式中:
session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
點開之後發現呼叫的是selectList();
繼續點進去
這裡是建立快取key, 這個 key 是一級快取 HashMap 的 key,由 MappedStatement 的 Id、SQL 的 offset、SQL 的 limit、SQL 本身以及 SQL 中的引數 Params 構成的:
Statement Id + Offset + Limmit + Sql + Params
驗證二級快取是否開啟,這裡沒有開啟,所以跳過if中的程式碼,執行delegate.query:
org.apache.ibatis.executor.CachingExecutor#query()
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) {
//看是否需要清除cache(在xml中可以配置flushCache屬性決定何時清空cache)
this.flushCacheIfRequired(ms);
//若開啟了cache且resultHandler 為空
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, parameterObject, boundSql);
//從TransactionalCacheManager中取cache
List<E> list = (List)this.tcm.getObject(cache, key);
//若取出來list是空的
if (list == null) {
//查詢資料庫
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//將結果存入cache中
this.tcm.putObject(cache, key, list);
}
return list;
}
}
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
查詢一級快取:
org.apache.ibatis.executor.BaseExecutor#query()
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 (this.closed) { //一級快取預設開啟
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
list = resultHandler == null ? (List)this.localCache.getObject(key) : null; //第一次會從本地快取通過key獲取值
if (list != null) { //如果不為空,則從本地獲取
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else { //如果為空,則查詢資料庫
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
因此,mybatis的快取是先驗證二級快取,再驗證一級快取
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER); //首先設定了一個預設值放入localCache
List list;
try {
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql); //執行查詢
} finally {
this.localCache.removeObject(key); //刪除原來的值
}
this.localCache.putObject(key, list); //將查詢到的值放到快取中
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list; //返回查詢的結果
}
這個方法,首先設定了一個預設值放入localCache,查詢完成後刪除key值,然後將查詢結果list放入快取localCache
所以當第二次執行查詢操作時,發現key值相同,就會到一級快取中去查詢,這樣就會出現日誌中查詢兩次,但是隻會執行一次資料庫操作的現象了。
並且當commit或者rollback的時候會清除快取,並且當執行insert、update、delete的時候也會清除快取。
相關原始碼:
org.apache.ibatis.executor.BaseExecutor#update
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
//刪除一級快取
this.clearLocalCache();
return this.doUpdate(ms, parameter);
}
}
org.apache.ibatis.executor.BaseExecutor#commit
public void commit(boolean required) throws SQLException {
if (this.closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
} else {
//刪除一級快取
this.clearLocalCache();
this.flushStatements();
if (required) {
this.transaction.commit();
}
}
}
org.apache.ibatis.executor.BaseExecutor#rollback
public void rollback(boolean required) throws SQLException {
if (!this.closed) {
try {
//刪除一級快取
this.clearLocalCache();
this.flushStatements(true);
} finally {
if (required) {
this.transaction.rollback();
}
}
}
}
區別:
儲存結構, 一級快取是存在記憶體 Map 中。二級快取儲存介質多樣,可在記憶體、硬碟中,需要進行序列化和反序列化;
範圍, 一級快取是 sqlSession 級別的快取,不同的sqlSession之間的快取是共享的,每個 SqlSession 都會建立一個 Executor,每個 Executor 都有一個一級快取 LocalCache,二級快取是namespace級別,跨 SqlSession 的;
失效場景, 一級、二級快取都是在執行插入、更新、刪除時會失效,需要重新從資料庫獲取,避免髒讀。另外一級快取不能用於分散式場景,二級快取需要使用 redis 來實現;
相關文章
- 【MyBatis原始碼解析】MyBatis一二級快取MyBatis原始碼快取
- mybatis原始碼學習:一級快取和二級快取分析MyBatis原始碼快取
- myBatis原始碼解析-二級快取的實現方式MyBatis原始碼快取
- MyBatis快取機制(一級快取,二級快取)MyBatis快取
- Mybatis的快取——一級快取和原始碼分析MyBatis快取原始碼
- mybatis快取-二級快取MyBatis快取
- Mybatis 一級快取和二級快取原理區別 (圖文詳解)MyBatis快取
- mybatis快取之一級快取(二)MyBatis快取
- myBatis原始碼解析-快取篇(2)MyBatis原始碼快取
- MyBatis中的一級快取和二級快取介紹MyBatis快取
- Mybatis的二級快取、使用Redis做二級快取MyBatis快取Redis
- Mybatis二級快取使用MyBatis快取
- Mybatis的二級快取MyBatis快取
- Mybatis一級快取MyBatis快取
- mybatis二級快取引數MyBatis快取
- Hibernate中一級快取和二級快取使用詳解快取
- Mybatis 二級快取應用 (21)MyBatis快取
- mybatis快取之一級快取(一)MyBatis快取
- MyBatis詳細原始碼解析(上篇)MyBatis原始碼
- 被mybatis一級快取坑了MyBatis快取
- spring boot redis做mybatis二級快取Spring BootRedisMyBatis快取
- mybatis基礎系列(四)——關聯查詢、延遲載入、一級快取與二級快取MyBatis快取
- Hibernate的一級快取Session和二級快取SessionFactory快取Session
- Spring Boot + Mybatis + Redis二級快取例項Spring BootMyBatisRedis快取
- mybatis二級快取應用及與ehcache整合MyBatis快取
- Mybatis一級快取和結合Spring Framework後失效的原始碼探究MyBatis快取SpringFramework原始碼
- hibernate(二)一級快取和三種狀態解析快取
- Spring Boot + Mybatis + Redis二級快取開發指南SpringbootMyBatisRedis快取
- MyBatis快取原始碼分析MyBatis快取原始碼
- Spring 的迴圈依賴,原始碼詳細分析 → 真的非要三級快取嗎Spring原始碼快取
- Hibernate一級快取(session)與二級快取(sessionFactory)的知識點。快取Session
- hibernate(九) 二級快取和事務級別詳講快取
- Mybatis 一級快取和引發的問題MyBatis快取
- MybatisPlus二級快取MyBatis快取
- 深入理解 MyBatis的二級快取的設計原理MyBatis快取
- Mybatis整合二級快取與同時使用快取與事務存在的坑MyBatis快取
- mybatis一級快取(session cache)引發的問題MyBatis快取Session
- Laravel 實現二級快取 提高快取的命中率和細粒化快取 keyLaravel快取