MyBatis二級(jí)緩存

一滴劲、MyBatis緩存官網(wǎng)內(nèi)容

MyBatis-官網(wǎng)-緩存

1.1 緩存

MyBatis 內(nèi)置了一個(gè)強(qiáng)大的事務(wù)性查詢緩存機(jī)制,它可以非常方便地配置和定制。 為了使它更加強(qiáng)大而且易于配置划址,我們對(duì) MyBatis 3 中的緩存實(shí)現(xiàn)進(jìn)行了許多改進(jìn)梦抢。默認(rèn)情況下,只啟用了本地的會(huì)話緩存限嫌,它僅僅對(duì)一個(gè)會(huì)話(SqlSession)中的數(shù)據(jù)進(jìn)行緩存送粱。 要啟用全局的二級(jí)緩存褪贵,只需要在你的 SQL 映射文件中添加一行:

<cache/>

基本上就是這樣,這個(gè)簡單語句的效果如下:

  • 映射語句文件中的所有 select 語句的結(jié)果將會(huì)被緩存抗俄。
  • 映射語句文件中的所有 insert脆丁、update 和 delete 語句會(huì)刷新緩存。
  • 緩存會(huì)使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存动雹。
  • 緩存不會(huì)定時(shí)進(jìn)行刷新(也就是說槽卫,沒有刷新間隔)。
  • 緩存會(huì)保存列表或?qū)ο螅o論查詢方法返回哪種)的 1024 個(gè)引用洽胶。
  • 緩存會(huì)被視為讀/寫緩存晒夹,這意味著獲取到的對(duì)象并不是共享的,可以安全地被調(diào)用者修改姊氓,而不干擾其他調(diào)用者或線程所做的潛在修改。

緩存只作用于 cache 標(biāo)簽所在的映射文件中的語句喷好。如果你混合使用 Java API 和 XML 映射文件翔横,在共用接口中的語句將不會(huì)被默認(rèn)緩存。你需要使用@CacheNamespaceRef 注解指定緩存作用域梗搅。這些屬性可以通過 cache 元素的屬性來修改禾唁。比如:

<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>

這個(gè)更高級(jí)的配置創(chuàng)建了一個(gè) FIFO 緩存,每隔 60 秒刷新无切,最多可以存儲(chǔ)結(jié)果對(duì)象或列表的 512 個(gè)引用荡短,而且返回的對(duì)象被認(rèn)為是只讀的,因此對(duì)它們進(jìn)行修改可能會(huì)在不同線程中的調(diào)用者產(chǎn)生沖突哆键【蛲校可用的清除策略有:

  • LRU – 最近最少使用:移除最長時(shí)間不被使用的對(duì)象。
  • FIFO – 先進(jìn)先出:按對(duì)象進(jìn)入緩存的順序來移除它們籍嘹。
  • SOFT – 軟引用:基于垃圾回收器狀態(tài)和軟引用規(guī)則移除對(duì)象闪盔。
  • WEAK – 弱引用:更積極地基于垃圾收集器狀態(tài)和弱引用規(guī)則移除對(duì)象弯院。
    默認(rèn)的清除策略是 LRU。

flushInterval(刷新間隔)屬性可以被設(shè)置為任意的正整數(shù)泪掀,設(shè)置的值應(yīng)該是一個(gè)以毫秒為單位的合理時(shí)間量听绳。 默認(rèn)情況是不設(shè)置,也就是沒有刷新間隔异赫,緩存僅僅會(huì)在調(diào)用語句時(shí)刷新椅挣。

size(引用數(shù)目)屬性可以被設(shè)置為任意正整數(shù),要注意欲緩存對(duì)象的大小和運(yùn)行環(huán)境中可用的內(nèi)存資源塔拳。默認(rèn)值是 1024贴妻。

readOnly(只讀)屬性可以被設(shè)置為 true 或 false。只讀的緩存會(huì)給所有調(diào)用者返回緩存對(duì)象的相同實(shí)例蝙斜。 因此這些對(duì)象不能被修改名惩。這就提供了可觀的性能提升。而可讀寫的緩存會(huì)(通過序列化)返回緩存對(duì)象的拷貝孕荠。 速度上會(huì)慢一些娩鹉,但是更安全,因此默認(rèn)值是 false稚伍。

二級(jí)緩存是事務(wù)性的弯予。這意味著,當(dāng) SqlSession 完成并提交時(shí)个曙,或是完成并回滾锈嫩,但沒有執(zhí)行 flushCache=true 的 insert/delete/update 語句時(shí),緩存會(huì)獲得更新垦搬。

1.2 使用自定義緩存

除了上述自定義緩存的方式呼寸,你也可以通過實(shí)現(xiàn)你自己的緩存,或?yàn)槠渌谌骄彺娣桨竸?chuàng)建適配器猴贰,來完全覆蓋緩存行為对雪。

<cache type="com.domain.something.MyCustomCache"/>

這個(gè)示例展示了如何使用一個(gè)自定義的緩存實(shí)現(xiàn)。type 屬性指定的類必須實(shí)現(xiàn) org.apache.ibatis.cache.Cache 接口米绕,且提供一個(gè)接受 String 參數(shù)作為 id 的構(gòu)造器瑟捣。 這個(gè)接口是 MyBatis 框架中許多復(fù)雜的接口之一,但是行為卻非常簡單栅干。

public interface Cache {
  String getId();
  int getSize();
  void putObject(Object key, Object value);
  Object getObject(Object key);
  boolean hasKey(Object key);
  Object removeObject(Object key);
  void clear();
}

為了對(duì)你的緩存進(jìn)行配置迈套,只需要簡單地在你的緩存實(shí)現(xiàn)中添加公有的 JavaBean 屬性,然后通過 cache 元素傳遞屬性值碱鳞,例如桑李,下面的例子將在你的緩存實(shí)現(xiàn)上調(diào)用一個(gè)名為 setCacheFile(String file) 的方法:

<cache type="com.domain.something.MyCustomCache">
  <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>

你可以使用所有簡單類型作為 JavaBean 屬性的類型,MyBatis 會(huì)進(jìn)行轉(zhuǎn)換。 你也可以使用占位符(如 ${cache.file})芙扎,以便替換成在配置文件屬性中定義的值星岗。從版本 3.4.2 開始,MyBatis 已經(jīng)支持在所有屬性設(shè)置完畢之后戒洼,調(diào)用一個(gè)初始化方法俏橘。 如果想要使用這個(gè)特性,請(qǐng)?jiān)谀愕淖远x緩存類里實(shí)現(xiàn)org.apache.ibatis.builder.InitializingObject 接口圈浇。

public interface InitializingObject {
  void initialize() throws Exception;
}

上一節(jié)中對(duì)緩存的配置(如清除策略寥掐、可讀或可讀寫等),不能應(yīng)用于自定義緩存磷蜀。

請(qǐng)注意召耘,緩存的配置和緩存實(shí)例會(huì)被綁定到 SQL 映射文件的命名空間中。 因此褐隆,同一命名空間中的所有語句和緩存將通過命名空間綁定在一起污它。 每條語句可以自定義與緩存交互的方式,或?qū)⑺鼈兺耆懦诰彺嬷馐@可以通過在每條語句上使用兩個(gè)簡單屬性來達(dá)成衫贬。 默認(rèn)情況下,語句會(huì)這樣來配置:

<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

鑒于這是默認(rèn)行為歇攻,顯然你永遠(yuǎn)不應(yīng)該以這樣的方式顯式配置一條語句固惯。但如果你想改變默認(rèn)的行為,只需要設(shè)置 flushCache 和 useCache 屬性缴守。比如葬毫,某些情況下你可能希望特定 select 語句的結(jié)果排除于緩存之外,或希望一條 select 語句清空緩存屡穗。類似地贴捡,你可能希望某些 update 語句執(zhí)行時(shí)不要刷新緩存。

1.3 cache-ref

回想一下上一節(jié)的內(nèi)容鸡捐,對(duì)某一命名空間的語句栈暇,只會(huì)使用該命名空間的緩存進(jìn)行緩存或刷新。 但你可能會(huì)想要在多個(gè)命名空間中共享相同的緩存配置和實(shí)例箍镜。要實(shí)現(xiàn)這種需求,你可以使用 cache-ref 元素來引用另一個(gè)緩存煎源。

<cache-ref namespace="com.someone.application.data.SomeMapper"/>

二色迂、補(bǔ)充

2.1 Mybatis緩存介紹

Mybatis提供查詢緩存,如果緩存中有數(shù)據(jù)就不用從數(shù)據(jù)庫中獲取手销,用于減輕數(shù)據(jù)壓力歇僧,提高系統(tǒng)性能。Mybatis的查詢緩存總共有兩級(jí),我們稱之為一級(jí)緩存和二級(jí)緩存诈悍。

一級(jí)緩存是SqlSession級(jí)別的緩存祸轮。在操作數(shù)據(jù)庫時(shí)需要構(gòu)造 sqlSession對(duì)象,在對(duì)象中有一個(gè)數(shù)據(jù)結(jié)構(gòu)(HashMap)用于存儲(chǔ)緩存數(shù)據(jù)侥钳。不同的sqlSession之間的緩存數(shù)據(jù)區(qū)域(HashMap)是互相不影響的适袜。

二級(jí)緩存是Mapper(namespace)級(jí)別的緩存。多個(gè)SqlSession去操作同一個(gè)Mapper的sql語句舷夺,多個(gè)SqlSession可以共用二級(jí)緩存苦酱,二級(jí)緩存是跨SqlSession的。

2.2 一級(jí)緩存

Mybatis的一級(jí)緩存是默認(rèn)開啟的给猾,其實(shí)指的是Mybaits中SqlSession對(duì)象的緩存疫萤。當(dāng)我們執(zhí)行查詢之后,查詢的結(jié)果會(huì)同時(shí)存入到 SqlSession為我們提供的一塊區(qū)域中敢伸。該區(qū)域的結(jié)構(gòu)是一個(gè)Map扯饶。當(dāng)我們?cè)俅尾樵兺瑯拥臄?shù)據(jù),Mybatis會(huì)先去SqlSession中查詢是否存在池颈,存在的話直接返回尾序,而不會(huì)再去數(shù)據(jù)庫查詢。這里要注意一個(gè)點(diǎn)饶辙,因?yàn)橐患?jí)緩存是依賴于SqlSession對(duì)象的蹲诀,當(dāng)SqlSession對(duì)象消失時(shí),Mybaits的一級(jí)緩存也就消失了弃揽。

當(dāng)Mybaits與Spring整合的時(shí)候脯爪,不在Spring事務(wù)的方法內(nèi),每次請(qǐng)求數(shù)據(jù)庫矿微,都會(huì)新建一個(gè)SqlSession痕慢,這時(shí)候是使用不到一級(jí)緩存的,代碼示例如下:

public void test1(){
    MailConf mail1 = mailConfService.findByMailCode("11");
    System.out.println(mail1);
    MailConf mail2 = mailConfService.findByMailCode("11");
    System.out.println(mail2);
    System.out.println(mail1 == mail2);
}

可以看到涌矢,每次請(qǐng)求都新建了一個(gè)Sqlsession掖举,返回的對(duì)象也不是同一個(gè)。

在帶Spring事務(wù)的方法內(nèi)娜庇,每次請(qǐng)求數(shù)據(jù)庫塔次,只新建一個(gè)SqlSession,這時(shí)候是可以使用到一級(jí)緩存的名秀,代碼示例如下:

@Transactional(rollbackFor = Exception.class)
public void test1(){
    MailConf mail1 = mailConfService.findByMailCode("11");
    System.out.println(mail1);
    MailConf mail2 = mailConfService.findByMailCode("11");
    System.out.println(mail2);
    System.out.println(mail1 == mail2);
}

這樣就用到了一級(jí)緩存励负。

除了事務(wù)問題,還有一些問題也會(huì)影響到一級(jí)緩存匕得,比如調(diào)用了Sqlsession的修改继榆、添加、刪除、commit()略吨、close()等方法時(shí)集币,一級(jí)緩存也會(huì)被清空,底下示例就先刪除一條記錄翠忠,然后看下結(jié)果:

@Transactional(rollbackFor = Exception.class)
public void test1() {
    MailConf mail1 = mailConfService.findByMailCode("11");
    System.out.println(mail1);
    // 刪除
    mailConfService.deleteConf(2);
    MailConf mail2 = mailConfService.findByMailCode("11");
    System.out.println(mail2);
    System.out.println(mail1 == mail2);
}

同樣的代碼鞠苟,不同的執(zhí)行結(jié)果,這時(shí)候也是沒有用到一級(jí)緩存负间。只要不是放在兩次查詢的中間偶妖,都會(huì)使用到一級(jí)緩存。

2.2 二級(jí)緩存
2.2.1 開啟二級(jí)緩存

Mybatis的二級(jí)緩存是默認(rèn)關(guān)閉的政溃,它指的是MybatisSqlSessionFactory對(duì)象的緩存趾访,由同一個(gè)SqlSessionFactory對(duì)象創(chuàng)建的SqlSession共享其二級(jí)緩存。這里注意:一個(gè)SqlSessionFactory對(duì)象可能創(chuàng)建多個(gè)SqlSession董虱。

SpringBoot項(xiàng)目中二級(jí)緩存使用步驟:

  • 讓當(dāng)前的映射文件支持二級(jí)緩存(在xxx.xml中配置)
  • 讓當(dāng)前的操作支持二級(jí)緩存(在select標(biāo)簽中配置)
<settings>
  <setting "cacheEnabled"value="true"/>
</settings>

mapper.xml中加入標(biāo)簽<cache/>核心參數(shù)可不設(shè)置扼鞋,使用默認(rèn)參數(shù)。開啟本mapper下的namespace的二級(jí)緩存愤诱,默認(rèn)使用的是mybatis提供的PerpetualCache云头。

第一次調(diào)用mapper下的SQL去查詢用戶信息。查詢到的信息會(huì)存到該mapper對(duì)應(yīng)的二級(jí)緩存區(qū)域內(nèi)淫半。

第二次調(diào)用相同namespace下的mapper映射文件中相同的SQL去查詢用戶信息溃槐。會(huì)去對(duì)應(yīng)的二級(jí)緩存內(nèi)取結(jié)果。如果調(diào)用相同namespace下的mapper映射文件中的增刪改SQL科吭,并執(zhí)行了commit操作昏滴。此時(shí)會(huì)清空該namespace下的二級(jí)緩存。

2.2.2 禁用二級(jí)緩存

默認(rèn)二級(jí)緩存的粒度是mapper級(jí)別的对人,但是如果在同一個(gè)mapper文件中某個(gè)查詢不想使用二級(jí)緩存的話谣殊,就需要對(duì)緩存的控制粒度更細(xì)。在select標(biāo)簽中設(shè)置useCache=false牺弄,可以禁用當(dāng)前select語句的二級(jí)緩存姻几,即每次查詢都是去數(shù)據(jù)庫中查詢,默認(rèn)情況下是true势告,即該statement使用二級(jí)緩存蛇捌。

<select id="findUserById" 
parameterType="int" resultType="com.kkb.mybatis.po.User"useCache="true">
    SELECT * FROM user WHERE id = #{id}
</select>

2.2.3 刷新二級(jí)緩存

通過flushCache屬性,可以控制select咱台、insert豁陆、update、delete標(biāo)簽是否屬性二級(jí)緩存吵护。

默認(rèn)情況下如果是select語句,那么flushCachefalse。如果是insert馅而、update祥诽、delete語句,那么flushCachetrue瓮恭。

如果查詢語句設(shè)置成true雄坪,那么每次查詢都是去數(shù)據(jù)庫查詢,即意味著該查詢的二級(jí)緩存失效屯蹦。

如果增刪改語句設(shè)置成false维哈,即使用二級(jí)緩存,那么如果在數(shù)據(jù)庫中修改了數(shù)據(jù)登澜,而緩存數(shù)據(jù)還是原來的阔挠,這個(gè)時(shí)候就會(huì)出現(xiàn)臟讀。

flushCache設(shè)置如下:

<selectid="findUserById"parameterType="int" resultType="com.kkb.mybatis.po.User" useCache="true" flushCache="true">
   SELECT * FROM user WHERE id =#{id}
</select>
2.3 為什么使用緩存

使用緩存可以減少和數(shù)據(jù)庫的交互次數(shù)脑蠕,提高執(zhí)行效率购撼,并且可以保護(hù)數(shù)據(jù)庫不被高并發(fā)的請(qǐng)求拖死。

2.3 適用于緩存的數(shù)據(jù)
  • 經(jīng)常查詢并且不經(jīng)常被改變的數(shù)據(jù)谴仙。
  • 數(shù)據(jù)的正確與否對(duì)最終結(jié)果影響不大的數(shù)據(jù)迂求。
2.4 不適用于緩存的數(shù)據(jù)
  • 經(jīng)常改變的數(shù)據(jù)。
  • 數(shù)據(jù)的正確與否對(duì)最終結(jié)果影響很大的晃跺。
2.5 總結(jié)

1揩局、Mybatis的一級(jí)緩存默認(rèn)開啟,而二級(jí)緩存默認(rèn)關(guān)閉掀虎。

2凌盯、Mybatis的一級(jí)緩存指的是Mybaits中SqlSession對(duì)象的緩存,而二級(jí)緩存指的是SqlSessionFactory對(duì)象的緩存涩盾。一個(gè)SqlSessionFactory對(duì)象包括多個(gè)SqlSession對(duì)象十气。

3、SqlSession對(duì)象中存放的是返回?cái)?shù)據(jù)的對(duì)象春霍,而SqlSessionFactory對(duì)象中存放的是數(shù)據(jù)砸西,不是對(duì)象。

4址儒、Mybatis和Spring整合的時(shí)候芹枷,一級(jí)緩存與事務(wù)有關(guān),而二級(jí)緩存與事務(wù)無關(guān)莲趣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鸳慈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子喧伞,更是在濱河造成了極大的恐慌走芋,老刑警劉巖绩郎,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異翁逞,居然都是意外死亡肋杖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門挖函,熙熙樓的掌柜王于貴愁眉苦臉地迎上來状植,“玉大人,你說我怎么就攤上這事怨喘〗蚧” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵必怜,是天一觀的道長肉拓。 經(jīng)常有香客問我,道長棚赔,這世上最難降的妖魔是什么帝簇? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮靠益,結(jié)果婚禮上丧肴,老公的妹妹穿的比我還像新娘。我一直安慰自己胧后,他們只是感情好芋浮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著壳快,像睡著了一般纸巷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上眶痰,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天瘤旨,我揣著相機(jī)與錄音,去河邊找鬼竖伯。 笑死存哲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的七婴。 我是一名探鬼主播祟偷,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼打厘!你這毒婦竟也來了修肠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤户盯,失蹤者是張志新(化名)和其女友劉穎嵌施,沒想到半個(gè)月后饲化,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡艰管,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年滓侍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牲芋。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捺球,靈堂內(nèi)的尸體忽然破棺而出缸浦,到底是詐尸還是另有隱情,我是刑警寧澤氮兵,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布裂逐,位于F島的核電站,受9級(jí)特大地震影響泣栈,放射性物質(zhì)發(fā)生泄漏卜高。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一南片、第九天 我趴在偏房一處隱蔽的房頂上張望掺涛。 院中可真熱鬧,春花似錦疼进、人聲如沸薪缆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拣帽。三九已至,卻和暖如春嚼锄,著一層夾襖步出監(jiān)牢的瞬間减拭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國打工区丑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拧粪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓刊苍,卻偏偏與公主長得像既们,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子正什,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容