MyBatis是一個簡單,小巧但功能很是強大的ORM開源框架,它的功能強大也體如今它的緩存機制上。MyBatis提供了一級緩存、二級緩存 這兩個緩存機制,可以很好地處理和維護緩存,以提升系統的性能。本文將介紹MyBatis的一級緩存,並深刻源碼解析MyBatis一級緩存的實現原理。java
什麼是一級緩存?sql
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:787707172,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。數據庫
每當咱們使用MyBatis開啓一次和數據庫的會話,MyBatis會建立出一個SqlSession對象表示一次數據庫會話。緩存
在對數據庫的一次會話中,咱們有可能會反覆地執行徹底相同的查詢語句,若是不採起一些措施的話,每一次查詢都會查詢一次數據庫,而咱們在極短的時間內作了徹底相同的查詢,那麼它們的結果極有可能徹底相同,因爲查詢一次數據庫的代價很大,這有可能形成很大的資源浪費。架構
爲了解決這一問題,減小資源的浪費,MyBatis會在表示會話的SqlSession對象中創建一個簡單的緩存,將每次查詢到的結果結果緩存起來,當下次查詢的時候,若是判斷先前有個徹底同樣的查詢,會直接從緩存中直接將結果取出,返回給用戶,不須要再進行一次數據庫查詢了。框架
以下圖所示,MyBatis會在一次會話的表示(一個SqlSession對象)中建立一個本地緩存(local cache),對於每一次查詢,都會嘗試根據查詢的條件去本地緩存中查找是否在緩存中,若是在緩存中,就直接從緩存中取出,而後返回給用戶;不然,從數據庫讀取數據,將查詢結果存入緩存並返回給用戶。分佈式
對於會話(Session)級別的數據緩存,咱們稱之爲一級數據緩存,簡稱一級緩存。微服務
一級緩存的實現原理源碼分析
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:787707172,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。性能
因爲MyBatis使用SqlSession對象表示一次數據庫的會話,那麼,對於會話級別的一級緩存也應該是在SqlSession中控制的。
實際上, MyBatis只是一個MyBatis對外的接口,SqlSession將它的工做交給了Executor執行器這個角色來完成,負責完成對數據庫的各類操做。當建立了一個SqlSession對象時,MyBatis會爲這個SqlSession對象建立一個新的Executor執行器,而緩存信息就被維護在這個Executor執行器中,MyBatis將緩存和對緩存相關的操做封裝成了Cache接口中。SqlSession、Executor、Cache之間的關係以下列類圖所示:
如上述的類圖所示,Executor接口的實現類BaseExecutor中擁有一個Cache接口的實現類PerpetualCache,則對於BaseExecutor對象而言,它將使用PerpetualCache對象維護緩存。
因爲Session級別的一級緩存實際上就是使用PerpetualCache維護的,那麼PerpetualCache是怎樣實現的呢?
PerpetualCache實現原理其實很簡單,其內部就是經過一個簡單的HashMap<k,v> 來實現的,沒有其餘的任何限制。以下是PerpetualCache的實現代碼:
public class PerpetualCache implements Cache { private String id; private Map<Object, Object> cache = new HashMap<Object, Object>(); public PerpetualCache(String id) { this.id = id; } public String getId() { return id; } public int getSize() { return cache.size(); } public void putObject(Object key, Object value) { cache.put(key, value); } public Object getObject(Object key) { return cache.get(key); } public Object removeObject(Object key) { return cache.remove(key); } public void clear() { cache.clear(); } public ReadWriteLock getReadWriteLock() { return null; } public boolean equals(Object o) { if (getId() == null) throw new CacheException("Cache instances require an ID."); if (this == o) return true; if (!(o instanceof Cache)) return false; Cache otherCache = (Cache) o; return getId().equals(otherCache.getId()); } public int hashCode() { if (getId() == null) throw new CacheException("Cache instances require an ID."); return getId().hashCode(); } }
一級緩存的生命週期有多長?
a. MyBatis在開啓一個數據庫會話時,會 建立一個新的SqlSession對象,SqlSession對象中會有一個新的Executor對象,Executor對象中持有一個新的PerpetualCache對象;當會話結束時,SqlSession對象及其內部的Executor對象還有PerpetualCache對象也一併釋放掉。
b. 若是SqlSession調用了close()方法,會釋放掉一級緩存PerpetualCache對象,一級緩存將不可用;
c. 若是SqlSession調用了clearCache(),會清空PerpetualCache對象中的數據,可是該對象仍可以使用;
d.SqlSession中執行了任何一個update操做(update()、delete()、insert()) ,都會清空PerpetualCache對象的數據,可是該對象能夠繼續使用;
CacheKey的定義
咱們知道,Cache最核心的實現其實就是一個Map,將本次查詢使用的特徵值做爲key,將查詢結果做爲value存儲到Map中。
如今最核心的問題出現了:怎樣來肯定一次查詢的特徵值?
換句話說就是:怎樣判斷某兩次查詢是徹底相同的查詢?
也能夠這樣說:如何肯定Cache中的key值?
MyBatis認爲,對於兩次查詢,若是如下條件都徹底同樣,那麼就認爲它們是徹底相同的兩次查詢:
綜上所述,CacheKey由如下條件決定:statementId + rowBounds + 傳遞給JDBC的SQL + 傳遞給JDBC的參數值
歡迎工做一到八年的Java工程師朋友們加入Java高級交流:787707172
本羣提供免費的學習指導 架構資料 以及免費的解答
不懂得問題均可以在本羣提出來 以後還會有直播平臺和講師直接交流噢