一、MyBatis緩存機(jī)制
1.簡介
- Mybatis包含了一個(gè)非常強(qiáng)大的查詢緩存的特性卖氨,它可以非常方便地配置和定制坞淮。
- 緩存key極大提高查詢效率
- MyBatis系統(tǒng)中默認(rèn)定義了兩次緩存
- 默認(rèn)情況下当纱,只有一級緩存(SqlSession級別的緩存嘀略,也稱為本地緩存)開啟。
- 二級緩存需要手動開啟和配置乓诽,它是基于namespace級別的緩存帜羊。
- 為了提高擴(kuò)展性,MyBatis定義了緩存接口cache鸠天∷嫌可以通過實(shí)現(xiàn)Cache接口來自定義二級緩存。
2.一級緩存
- 一級緩存稠集,即本地緩存奶段,作用域默認(rèn)為SqlSession。當(dāng)Session flush或close后剥纷,該Session中的所有Cache將被清空痹籍。
- 本地緩存不能被關(guān)閉,但可以調(diào)用clearCache()來清空本地緩存晦鞋,或者改變緩存的作用域蹲缠。
- 在mybatis3.1之后棺克,可以配置本地緩存的作用域,在mybatis.xml中配置localCacheScope屬性线定。
-MyBatis利用本地緩存防止循環(huán)引用和加速重復(fù)嵌套查詢娜谊,默認(rèn)值為SESSION,這種情況下會緩存一個(gè)會話中執(zhí)行的所有查詢斤讥。若設(shè)置值為STATEMENT纱皆,本地會話僅用在語句執(zhí)行上,對相同SqlSession的不同調(diào)用將不會共享數(shù)據(jù)芭商。 - 同一個(gè)會話期間只要查詢過的數(shù)據(jù)都會被保存在當(dāng)前SqlSession的一個(gè)Map中
- key:hashCode+查詢的SqlId+編寫的sql查詢語句+參數(shù)
3.一級緩存失效情況
- 不同的SqlSession對應(yīng)不同的一級緩存
- 同一個(gè)SqlSession但是查詢條件不同
- 同一個(gè)SqlSession兩次執(zhí)行期間執(zhí)行了任何一次增刪改操作派草。
- 同一個(gè)SqlSession兩次查詢期間手動清空了緩存。
4.二級緩存
- 二級緩存蓉坎,全局作用域緩存
- 二級緩存默認(rèn)不開腔澳眷,需要手動配置。
- MyBatis提供二級緩存的接口以及實(shí)現(xiàn)蛉艾,緩存實(shí)現(xiàn)要求POJO實(shí)現(xiàn)Serializable接口钳踊。
- 二級緩存在SqlSession關(guān)閉或提交之后才會生效。
- 使用步驟:
- 全局配置文件中開啟二級緩存
- 需要使用二級緩存的映射文件處使用cache配置緩存
- POJO實(shí)現(xiàn)Serializable接口勿侯。
4.緩存相關(guān)屬性
eviction="FIFO" :緩存回收策略
- LRU(最近最少使用):移除最長時(shí)間不被使用的對象拓瞪。
- FIFO(先進(jìn)先出):按對象進(jìn)入緩存的順序來移除對象。
- SOFT(軟引用):移除基于垃圾回收期狀態(tài)和軟引用規(guī)則的對象助琐。
- WEAK(弱引用):更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象祭埂。
- 默認(rèn)的是LRUflushInterval:刷新間隔,單位毫秒
- 默認(rèn)情況下不設(shè)置兵钮,也就是沒有刷新間隔蛆橡,緩存僅僅調(diào)用語句時(shí)刷新。size:引用數(shù)目掘譬,正整數(shù)
- 代表緩存最多可以存儲多少個(gè)對象泰演,太大容易導(dǎo)致內(nèi)存溢出readOnly:只讀,true/false
- true:只讀緩存葱轩;會給所有調(diào)用者返回緩存對象的相同實(shí)例睦焕。這些對象不能被修改。這提供了很重要的性能優(yōu)勢靴拱。
- false:讀寫緩存垃喊,會返回緩存對象的拷貝(通過序列化)。這會慢一些袜炕,但是安全本谜,默認(rèn)為false。
5.緩存設(shè)置
全局setting的cacheEnable
- 配置二級緩存的開關(guān)妇蛀,一級緩存一直是打開的耕突。select標(biāo)簽的useCache屬性
- 配置這個(gè)select是否使用二級緩存笤成。一級緩存一直是使用的。sql標(biāo)簽的flushCache屬性:
- 增刪改默認(rèn)為true眷茁,sql執(zhí)行后炕泳,會清空一級和二級緩存。查詢默認(rèn)false上祈。sqlSession.claerCache()
- 用來清除一級緩存當(dāng)在某一個(gè)作用域(一級緩存/二級緩存/Namespace)進(jìn)行了增刪改操作化培遵,默認(rèn)該作用域下所有select中的緩存將被clear。
6.使用第三方緩存之后的查詢流程
- 查詢二級緩存登刺。
- 如果二級緩存沒有籽腕,查詢一級緩存。
- 一級緩存也沒有纸俭,查詢數(shù)據(jù)庫皇耗。
二、MyBatis工作原理示意圖
三揍很、MyBatis插件開發(fā)
1.MyBatis插件
- MyBatis在四大對象的創(chuàng)建過程中郎楼,都會有插件進(jìn)行介入,插件可以利用動態(tài)代理機(jī)制一層層的包裝目標(biāo)對象窒悔,而實(shí)現(xiàn)在目標(biāo)對象指向目標(biāo)方法之前進(jìn)行攔截的效果呜袁。
- MyBatis允許在已映射語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用。
- 默認(rèn)情況下简珠,MyBatis允許使用插件來攔截的方法包括:
Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
ParameterHandler(getParameterObject,setParameters)
ResultSetHandler(handlerResultSets,handlerOuutputParameters)
StatementHandler(prepare,parameterize,batch,update,query)
2.插件開發(fā)步驟
- 編寫插件實(shí)現(xiàn)Interceptor接口阶界,并使用@Intercepts注解完成插件簽名。
@Intercepts({
@Signature(type=StatementHandler.class,method="prepare",
args={Connection.class})
})
public class MyPlugin implements Interceptor{}
- 在全局配置文件中注冊插件
<plugins>
<plugin interceptor="com.desperado.plugin.MyPlugin">
<property name="username" value="tomcat"/>
</plugin>
</plugins>
3.插件原理
- 按照插件注冊聲明聋庵,按照插件配置順序調(diào)用插件plugin方法膘融,生成被攔截對象的動態(tài)代理。
- 多個(gè)插件依次生成目標(biāo)對象的代理對象祭玉,層層包裹托启,先聲明的先包裹,形成代理鏈攘宙。
- 目標(biāo)想法執(zhí)行時(shí)依次從外到內(nèi)執(zhí)行intercept方法。
- 多個(gè)情況下拐迁,我們往往需要在某個(gè)插件中分離出來目標(biāo)對象蹭劈,可以借助MyBatis提供的SystemMateObject類來進(jìn)行獲取最后一層的h以及target屬性的值。
4.Interceptor接口
- intercept:攔截目標(biāo)方法執(zhí)行线召。
- plugin:生成動態(tài)代理對象铺韧,可以使用MyBatis提供的Plugin類的wrap方法。
- setProperties:注入插件配置時(shí)設(shè)置的屬性缓淹。
5. 常用代碼
//1.分離代理對象哈打。由于會形成多次代理塔逃,所以需要通過while循環(huán)分離出最終被代理的對象,從而方便提取信息料仗。
MetaObject metaObject = SystemMetaObject.forObject(target);
while(metaObject.hasGetter("h")){
Object h = metaObject.getValue("h");
metaObject = SystemMetaObject.forObject(h);
}
//2.獲取到代理對象中包含的被代理的真實(shí)對象
Object obj = metaObject.getValue("target");
//3.獲取被代理對象的MetaObject方便進(jìn)行信息提取
metaObject forObject = SystemMetaObject.forObject(obj);
四湾盗、其他操作
1.分頁操作
- 分頁可以使用PageHelper插件。
- 使用步驟:
- 導(dǎo)入相關(guān)的依賴立轧,pagehelper.jar和jsqlparser.jar.
- 在Mybatis全局配置文件中配置分頁插件
- 使用PageHelper提供的方法進(jìn)行分頁
- 可以使用更強(qiáng)大的PageInfo封裝返回結(jié)果格粪。
2.批量操作
默認(rèn)的openSession()方法沒有參數(shù),使用它創(chuàng)建的SqlSession具有如下特性:
- 會開啟一個(gè)事務(wù)(也就是 不自動提交)
- 連接對象會從由活動環(huán)境配置的數(shù)據(jù)源實(shí)例得到氛改。
- 事務(wù)隔離級別將會使用驅(qū)動或數(shù)據(jù)源的默認(rèn)設(shè)置帐萎。
- 預(yù)處理語句不會被復(fù)用,也不會批量處理更新胜卤。openSession方法的ExecutorType類型的參數(shù)疆导,枚舉類型,取值如下:
- SIMPLE:這個(gè)執(zhí)行器類型不做特殊的事情(這是默認(rèn)裝配的)葛躏。它為每個(gè)語句的執(zhí)行創(chuàng)建一個(gè)新的預(yù)處理語句澈段。
- REUSE:這個(gè)執(zhí)行器類型會復(fù)用預(yù)處理語句。
- BATCH:這個(gè)執(zhí)行器會批量執(zhí)行所有更新語句紫新。批量操作就是使用MyBatis提供的BATCH類型的Executor進(jìn)行的均蜜,它的底層就是通過暫存sql的方式進(jìn)行的∶⒙剩可以保存sql到一定數(shù)量后發(fā)給數(shù)據(jù)庫執(zhí)行一次囤耳。
與Spring整合中。推薦額外配置一個(gè)可以專門用來執(zhí)行批量操作的sqlSession偶芍,需要使用的時(shí)候充择,注入配置的批量操作的SqlSession,通過它獲取到mapper映射器進(jìn)行操作匪蟀。
批量操作是在session.commit()以后才發(fā)送sql語句給數(shù)據(jù)庫進(jìn)行執(zhí)行的椎麦。
途觀想讓其提前執(zhí)行,可以使用sqlSession.flushStatements()方法材彪,讓其執(zhí)行刷到數(shù)據(jù)庫中進(jìn)行執(zhí)行观挎。
3.存儲過程
- 調(diào)用存儲過程
- select標(biāo)簽中statementType="CALLABLE".
- 標(biāo)簽體中調(diào)用語法
<select id="callProcedure" statementType="CALLABLE">
call procedure_name(#{param1},#{param2})
</select>
- mybatis對存儲過程的游標(biāo)提供了一個(gè)JdbcType=CURSOR的支持,可以智能的把游標(biāo)讀取到的數(shù)據(jù)段化,映射到我們聲明的結(jié)果集中嘁捷。
4.自定義TypeHandler處理枚舉
- 可以通過自定義TypeHandler的形式來設(shè)置參數(shù)或者取出結(jié)果集的時(shí)候自定義參數(shù)封裝策略。
- 步驟:
1. 實(shí)現(xiàn)TypeHandler接口或者繼承BaseTypeHandler显熏。
2. 使用@MappedType定義處理的Java類型雄嚣,使用@MappedJdbcTypes定義jdbcType類型。
3. 在定義結(jié)果集標(biāo)簽或者參數(shù)處理的時(shí)候聲明使用自定義TypeHandler進(jìn)行處理或者在全局配置TypeHandler要處理的javaType