MyBatis的一級快取實現詳解 及使用注意事項
0.寫在前面
MyBatis是一個簡單,小巧但功能非常強大的ORM開源框架,它的功能強大也體現在它的快取機制上。MyBatis提供了一級快取、二級快取 這兩個快取機制,能夠很好地處理和維護快取,以提高系統的效能。本文的目的則是向讀者詳細介紹MyBatis的一級快取,深入原始碼,解析MyBatis一級快取的實現原理,並且針對一級快取的特點提出了在實際使用過程中應該注意的事項。
1. 什麼是一級快取? 為什麼使用一級快取?
每當我們使用MyBatis開啟一次和資料庫的會話,MyBatis會創建出一個SqlSession物件表示一次資料庫會話。
在對資料庫的一次會話中,我們有可能會反覆地執行完全相同的查詢語句,如果不採取一些措施的話,每一次查詢都會查詢一次資料庫,而我們在極短的時間內做了完全相同的查詢,那麼它們的結果極有可能完全相同,由於查詢一次資料庫的代價很大,這有可能造成很大的資源浪費。
為了解決這一問題,減少資源的浪費,MyBatis會在表示會話的SqlSession物件中建立一個簡單的快取,將每次查詢到的結果結果快取起來,當下次查詢的時候,如果判斷先前有個完全一樣的查詢,會直接從快取中直接將結果取出,返回給使用者,不需要再進行一次資料庫查詢了。
如下圖所示,MyBatis會在一次會話的表示----一個SqlSession物件中建立一個本地快取(local cache),對於每一次查詢,都會嘗試根據查詢的條件去本地快取中查詢是否在快取中,如果在快取中,就直接從快取中取出,然後返回給使用者;否則,從資料庫讀取資料,將查詢結果存入快取並返回給使用者。
對於會話(Session)級別的資料快取,我們稱之為一級資料快取,簡稱一級快取。
2. MyBatis中的一級快取是怎樣組織的?(即SqlSession中的快取是怎樣組織的?)
由於MyBatis使用SqlSession物件表示一次資料庫的會話,那麼,對於會話級別的一級快取也應該是在SqlSession中控制的。
實際上,
MyBatis只是一個MyBatis對外的介面,SqlSession將它的工作交給了Executor執行器這個角色來完成,負責完成對資料庫的各種操作。當建立了一個SqlSession物件時,MyBatis會為這個SqlSession物件建立一個新的Executor執行器,而快取資訊就被維護在這個Executor執行器中,MyBatis將快取和對快取相關的操作封裝成了Cache介面中。SqlSession、Executor、Cache之間的關係如下列類圖所示:
如上述的類圖所示,Executor介面的實現類BaseExecutor中擁有一個Cache介面的實現類PerpetualCache,則對於BaseExecutor物件而言,它將使用PerpetualCache物件維護快取。
綜上,SqlSession物件、Executor物件、Cache物件之間的關係如下圖所示:
由於Session級別的一級快取實際上就是使用PerpetualCache維護的,那麼PerpetualCache是怎樣實現的呢?
PerpetualCache實現原理其實很簡單,其內部就是通過一個簡單的HashMap<k,v>
來實現的,沒有其他的任何限制。如下是PerpetualCache的實現程式碼:package org.apache.ibatis.cache.impl; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.cache.CacheException; /** * 使用簡單的HashMap來維護快取 * @author Clinton Begin */ 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(); } }
3.一級快取的生命週期有多長?
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物件的資料,但是該物件可以繼續使用;
4. SqlSession 一級快取的工作流程:
1.對於某個查詢,根據statementId,params,rowBounds來構建一個key值,根據這個key值去快取Cache中取出對應的key值儲存的快取結果;
2. 判斷從Cache中根據特定的key值取的資料資料是否為空,即是否命中;
3. 如果命中,則直接將快取結果返回;
4. 如果沒命中:
4.1 去資料庫中查詢資料,得到查詢結果;
4.2 將key和查詢到的結果分別作為key,value對儲存到Cache中;
4.3. 將查詢結果返回;
5. 結束。
[關於上述工作過程中 key值的構建,我們將在第下一節中重點探討,這也是MyBatis快取機制中非常重要的一個概念。]
5. Cache介面的設計以及CacheKey的定義(非常重要)
如下圖所示,MyBatis定義了一個org.apache.ibatis.cache.Cache介面作為其Cache提供者的SPI(Service
Provider Interface) ,所有的MyBatis內部的Cache快取,都應該實現這一介面。MyBatis定義了一個PerpetualCache實現類實現了Cache介面,實際上,在SqlSession物件裡的Executor
物件內維護的Cache型別例項物件,就是PerpetualCache子類建立的。(MyBatis內部還有很多Cache介面的實現,一級快取只會涉及到這一個PerpetualCache子類,Cache的其他實現將會放到二級快取中介紹)。
我們知道,Cache最核心的實現其實就是一個Map,將本次查詢使用的特徵值作為key,將查詢結果作為value儲存到Map中。
現在最核心的問題出現了:怎樣來確定一次查詢的特徵值?
換句話說就是:怎樣判斷某兩次查詢是完全相同的查詢?
也可以這樣說:如何確定Cache中的key值?
MyBatis認為,對於兩次查詢,如果以下條件都完全一樣,那麼就認為它們是完全相同的兩次查詢:
1. 傳入的
statementId2. 查詢時要求的結果集中的結果範圍 (結果的範圍通過rowBounds.offset和rowBounds.limit表示);
3. 這次查詢所產生的最終要傳遞給JDBC java.sql.Preparedstatement的Sql語句字串(boundSql.getSql()
)4. 傳遞給java.sql.Statement要設定的引數值
現在分別解釋上述四個條件:
1. 傳入的statementId,對於MyBatis而言,你要使用它,必須需要一個statementId,它代表著你將執行什麼樣的Sql;
2.
MyBatis自身提供的分頁功能是通過RowBounds來實現的,它通過rowBounds.offset和rowBounds.limit來過濾查詢出來的結果集,這種分頁功能是基於查詢結果的再過濾,而不是進行資料庫的物理分頁;由於MyBatis底層還是依賴於JDBC實現的,那麼,對於兩次完全一模一樣的查詢,MyBatis要保證對於底層JDBC而言,也是完全一致的查詢才行。而對於JDBC而言,兩次查詢,只要傳入給JDBC的SQL語句完全一致,傳入的引數也完全一致,就認為是兩次查詢是完全一致的。
上述的第3個條件正是要求保證傳遞給JDBC的SQL語句完全一致;第4條則是保證傳遞給JDBC的引數也完全一致;
3、4講的有可能比較含糊,舉一個例子:
<select id="selectByCritiera" parameterType="java.util.Map" resultMap="BaseResultMap"> select employee_id,first_name,last_name,email,salary from louis.employees where employee_id = #{employeeId} and first_name= #{firstName} and last_name = #{lastName} and email = #{email} </select>
如果使用上述的"selectByCritiera"進行查詢,那麼,MyBatis會將上述的SQL中的#{}都替換成 ? 如下:
select employee_id,first_name,last_name,email,salary from louis.employees where employee_id = ? and first_name= ? and last_name = ? and email = ?
MyBatis最終會使用上述的SQL字串建立JDBC的java.sql.PreparedStatement物件,對於這個PreparedStatement物件,還需要對它設定引數,呼叫setXXX()來完成設值,第4條的條件,就是要求對設定JDBC的PreparedStatement的引數值也要完全一致。
即3、4兩條MyBatis最本質的要求就是:
呼叫JDBC的時候,傳入的SQL語句要完全相同,傳遞給JDBC的引數值也要完全相同。綜上所述,CacheKey由以下條件決定:
statementId + rowBounds + 傳遞給JDBC的SQL + 傳遞給JDBC的引數值
一級快取的效能分析
我將從兩個 一級快取的特性來討論SqlSession的一級快取效能問題:
1.MyBatis對會話(Session)級別的一級快取設計的比較簡單,就簡單地使用了HashMap來維護,並沒有對HashMap的容量和大小進行限制。
讀者有可能就覺得不妥了:如果我一直使用某一個SqlSession物件查詢資料,這樣會不會導致HashMap太大,而導致 java.lang.OutOfMemoryError錯誤啊?
讀者這麼考慮也不無道理,不過MyBatis的確是這樣設計的。MyBatis這樣設計也有它自己的理由:
a. 一般而言SqlSession的生存時間很短。一般情況下使用一個SqlSession物件執行的操作不會太多,執行完就會消亡;
b. 對於某一個SqlSession物件而言,只要執行update操作(update、insert、delete),都會將這個SqlSession物件中對應的一級快取清空掉,所以一般情況下不會出現快取過大,影響JVM記憶體空間的問題;
c. 可以手動地釋放掉SqlSession物件中的快取。
2. 一級快取是一個粗粒度的快取,沒有更新快取和快取過期的概念
MyBatis的一級快取就是使用了簡單的相關推薦
MyBatis 的一級快取實現詳解及使用注意事項
轉自:https://blog.csdn.net/chenyao1994/article/details/79233725 0.寫在前面 MyBatis是一個簡單,小巧但功能非常強大的ORM開源框架,它的功能強大也體現在它的快取機制上。MyBatis提供了一級快取、二級快取 這兩個快取機制,
《深入理解mybatis原理(三)》 MyBatis的一級快取實現詳解 及使用注意事項
0.寫在前面 MyBatis是一個簡單,小巧但功能非常強大的ORM開源框架,它的功能強大也體現在它的快取機制上。MyBatis提供了一級快取、二級快取 這兩個快取機制,能夠很好地處理和維護快取,以提高系統的效能。本文的目的則是向讀者詳細介紹MyBatis的一級快取,深入原始碼,解析MyBa
MyBatis的一級快取實現詳解 及使用注意事項
0.寫在前面 MyBatis是一個簡單,小巧但功能非常強大的ORM開源框架,它的功能強大也體現在它的快取機制上。MyBatis提供了一級快取、二級快取 這兩個快取機制,能夠很好地處理和維護快取,以提高系統的效能。本文的目的則是向讀者詳細介紹MyBati
BottomSheet 的詳解及注意事項
android support library更新的比較快,使用了幾個控制元件挺不錯,不過今天只寫BottomSheet 。 OK,這個東西肯定很多人都沒聽過和用過,其實用起來特別方便和簡單,不過它的使用需要引入Behavior機制,別說你沒聽說過Behavi
《深入理解mybatis原理6》 MyBatis的一級緩存實現詳解 及使用註意事項
net 特征值 session 成了 bool common 周期 當下 csdn 《深入理解mybatis原理》 MyBatis的一級緩存實現詳解 及使用註意事項 0.寫在前面 MyBatis是一個簡單,小巧但功能非常強大的ORM開源框架,它的功能強大也體現在它的
雜談——Mybatis一二級快取簡單詳解
先說快取,合理使用快取是優化中最常見。將從資料庫中查詢出來的資料放入快取(記憶體)中,每次查詢資料時,先判斷快取區中是否存在資料,如果存在,就從快取區中獲取資料,如果不存在,就從資料庫中獲取資料,將資料存放到快取區中。這樣的話,下次訪問該資料的時候使就不必從資料庫查詢,而是直接從快取中讀取,避免頻繁
MyBatis 二級快取全詳解
目錄 MyBatis 二級快取介紹 二級快取開啟條件 探究二級快取 二級快取失效的條件 第一次SqlSession 未提交 更新對二級快取影
MyBatis特快取性詳解
## 快取簡介 一般我們在系統中使用快取技術是為了提升資料查詢的效率。當我們從資料庫中查詢到一批資料後將其放入到混存中(簡單理解就是一塊記憶體區域),下次再查詢相同資料的時候就直接從快取中獲取資料就行了。這樣少了一步和資料庫的互動,可以提升查詢的效率。 但是一個硬幣都具有兩面性,快取在帶來效能提升的同時也
PAT甲級 Rational Sum(20) python實現 解題思路及注意事項
時間限制 1000 ms 記憶體限制 65536 KB 程式碼長度限制 100 KB 判斷程式 題目描述 Given N rational numbers in the form "numerator/denominator", you are supposed to
tcp連線關閉詳解和注意事項
注:tcp關閉連線不區分客戶端和服務端,哪一埠可以主動發起關閉連線請求。所以為了描述方便,描述中的“主動方”表示主動發起關閉連線一方,“被動方”表示被動關閉連線一方。 1. tcp關閉連線狀態轉換 上圖是tcp連線主動關閉端的狀態轉換圖: (1)應用層呼叫close函式發起關閉連線請求 (2
iOS中使用dispatch_once實現單例及注意事項
單例模式,是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中,應用該模式的類一個類只有一個例項。即一個類只有一個物件例項。 在iOS中單例模式實現方式是在類中編寫名為sharedInstance的方法,該方法只會返回全類共
React生命週期函式詳解和注意事項
React生命週期函式 生命週期函式是指在某一個週期自動執行的函式。 React中的生命週期執行過程 以下是React中的常用的生命週期函式,按個部分中按照自動執行順序列出,這幾個過程
MyBatis一級快取和二級快取詳解
一級快取 Mybatis對快取提供支援,但是在沒有配置的預設情況下,它只開啟一級快取,一級快取只是相對於同一個SqlSession而言。所以在引數和SQL完全一樣的情況下,我們使用同一個SqlSession物件呼叫一個Mapper方法,往往只執行一次SQL,因為使用SelSession第一次
cpu二級快取和一級快取詳解及區別(圖解)
錯誤觀點二:處理器快取是一個整體 【圖】生產技術對快取容量大小的影響 【圖】PCB上帶快取的PentiumIII處理器 事實上最早先的CPU快取確實是個整體,而且容量也很低。英特爾公司從Pentium時代開始後就把快取進行了分類,當時整合在CPU核心中的快取已不足以滿足CPU的需求,而製造工藝上
MyBatis 一級快取、二級快取全詳解(一)
目錄 MyBatis 一級快取、二級快取全詳解(一) 什麼是快取 什麼是MyBatis中的快取 MyBatis 中的一級快取 初探一級快取 探究一級快取是如何失效的
redis配置文件詳解及實現主從同步切換
redis redis主從 redis配置文件詳解及實現主從同步切換redis復制Redis復制很簡單易用,它通過配置允許slave Redis Servers或者Master Servers的復制品。接下來有幾個關於redis復制的非常重要特性:一個Master可以有多個Slaves。Slaves能
MyBatis之Mapper詳解及常用技巧
mybatis mapperselect先看一個簡單的案例:<select id="selectPerson" parameterType="int" resultType="hashmap"> SELECT * FROM PERSON WHERE ID = #{id} </selec
平面二維任意橢圓數據擬合算法推導及程序實現詳解
tar 擬合 推導 oai http margin oci 二維 詳解 0墾oe辟煽6味h幼瀾6http://t.docin.com/laiys67141 x0涸tj妝06曬妒http://t.docin.com/sina_6267117010 9剿襖2閱b盎忍40ht
微信和支付寶支付模式詳解及實現二
配置 其余 logs https 朋友 一個 target 多租戶 對比 繼上篇《微信和支付寶支付模式詳解及實現》到現在已經有半年時間了,這期間不少朋友在公號留言支付相關的問題,最近正好也在處理公司支付相關的對接,打算寫這篇來做一個更進一步的介紹,同時根據主要的幾個支付
NAT功能詳解及案例分析—華為NAT server的實現
NATNAT #network address translation - 網絡地址轉換。 NAT的分類 #靜態NAT、動態NAT,動態NAT包含了我們常用的PNAT(PAT)。 端口nat (端口地址轉換 華為&思科NAT對比 靜態NAT #靜態轉換是指將內部網絡的私有IP地址轉換為公有IP地址