Hibernate二級緩存

緩存(Cache):計算機(jī)領(lǐng)域的通用概念缀踪。它介于應(yīng)用程序和永久性數(shù)據(jù)存儲源(如硬盤上的數(shù)據(jù)庫或文件)之間蜻韭。其作用是減少應(yīng)用程序直接讀寫永久性數(shù)據(jù)存儲源的頻率臀栈,從而提高應(yīng)用程序的運(yùn)行性能月腋。緩存中的數(shù)據(jù)是數(shù)據(jù)存儲源中的數(shù)據(jù)的拷貝妄迁。緩存的物理介質(zhì)通常是內(nèi)存疗韵。

Hibernate中提供了兩個級別的緩存:

  • 第一級別的緩存是Session級別的緩存兑障,它屬于事務(wù)范圍內(nèi)的緩存。這一級別的緩存由Hibernate進(jìn)行管理蕉汪。
  • 第二級別的緩存是SessionFactory級別的緩存流译,它是屬于進(jìn)程范圍內(nèi)的緩存。

SessionFactory級別的緩存

SessionFactory的緩存分為兩類:

  • 內(nèi)置緩存:Hibernate自帶的者疤,不可卸載福澡。通常在Hibernate初始化階段,Hibernate會把映射元數(shù)據(jù)和預(yù)定義的SQL語句放到SessionFactory的緩存中驹马,映射元數(shù)據(jù)是映射文件(.hbm.xml)中的數(shù)據(jù)的復(fù)制革砸,該內(nèi)置緩存是只讀的。
  • 外置緩存(二級緩存):一個可配置的緩存插件糯累,在默認(rèn)情況下算利,SessionFactory不會啟動這個緩存插件,外置緩存中的數(shù)據(jù)是數(shù)據(jù)庫數(shù)據(jù)的復(fù)制寇蚊,外置緩存的物理介質(zhì)可以是內(nèi)存或硬盤笔时。

適合放入二級緩存的數(shù)據(jù):

  • 很少被修改
  • 不是很重要的數(shù)據(jù),允許出現(xiàn)偶爾的并發(fā)問題仗岸。

不適合放入二級緩存中的數(shù)據(jù)

  • 經(jīng)常被修改
  • 財務(wù)數(shù)據(jù)允耿,決不允許出現(xiàn)并發(fā)問題。
  • 與其它應(yīng)用程序共享的數(shù)據(jù)扒怖。
二級緩存的架構(gòu)

二級緩存的并發(fā)訪問策略

兩個并發(fā)的事務(wù)同時訪問持久層的緩存的相同的數(shù)據(jù)時较锡,也有可能出現(xiàn)各種各樣的并發(fā)問題。

二級緩存可以設(shè)定為以下四種類型的并發(fā)訪問策略盗痒,每一種訪問策略對應(yīng)一種事務(wù)的隔離級別蚂蕴。

  • 非嚴(yán)格讀寫(Nonstrict-read-write):不保證緩存與數(shù)據(jù)庫中的數(shù)據(jù)的一致性低散,提供Read-uncommited級別的數(shù)據(jù)庫隔離級別。對于極少修改而且允許臟讀的數(shù)據(jù)骡楼,可以采用這種策略熔号。
  • 讀寫型(Read-write):提供Read-commited數(shù)據(jù)隔離級別。對于經(jīng)常讀但很少修改的數(shù)據(jù)鸟整,可以使用這種隔離類型引镊,因為它可以防止臟讀。
  • 事務(wù)性(Transactional):僅在受管理環(huán)境下適用篮条,它提供了Repeatable-read事務(wù)隔離級別弟头。對于經(jīng)常讀但很少修改的數(shù)據(jù),可以采用這種隔離類型涉茧,因為它可以防止臟讀和不可重復(fù)讀赴恨。
  • 只讀型(Read-only):提供Serializable數(shù)據(jù)庫隔離類型,對于從來不會被修改的數(shù)據(jù)伴栓,可以采用這種訪問策略伦连。

配置進(jìn)程范圍內(nèi)的二級緩存

  • 選擇合適范圍的緩存插件:EHCache(jar包和配置),并編譯器配置文件挣饥。
  • 在Hibernate的配置文件中啟用二級緩存除师,并指定EHCache對應(yīng)的緩存適配器。
//啟動Hibernate的二級緩存
<property name="cache.use_second_level_cache">true</property>
//配置Hibernate二級緩存使用的產(chǎn)品
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
//配置對那些類使用Hibernate的二級緩存
<class-cache usage="read-write" class="com.hibernate.unionsubclass.Student"></class-cache>

也可以在.hbm.xml類中配置二級緩存扔枫。

  • 選擇需要使用二級緩存的持久化類汛聚,設(shè)置它的二級緩存的并發(fā)訪問策略
    • <class>元素的cache子元素表明Hibernate會緩存對象的簡單屬性,但不會緩存集合屬性短荐,如果希望緩存集合屬性倚舀,必須在<set>元素中加入<cache>子元素。
    • 在hibernate配置文件中通過<class-cache/>結(jié)點(diǎn)配置使用緩存忍宋。

設(shè)置集合緩存

我們看下面的代碼

SessionFactory sessionFactory = null;
Configuration cfg = new Configuration().configure();
sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();

Category category = session.get(Category.class, 42);
System.out.println(category.getItems().size());

transaction.commit();
session.close();

session = sessionFactory.openSession();
transaction = session.beginTransaction();
Category category2 = session.get(Category.class, 42);
System.out.println(category2.getItems().size());

transaction.commit();
session.close();
sessionFactory.close();

運(yùn)行后發(fā)現(xiàn)生成了四條SQL語句痕貌。此時我們給Category類添加入二級緩存。

<class-cache usage="read-write" class="com.hibernate.n2n.Category"></class-cache>

發(fā)現(xiàn)少了一條SQL語句糠排,但在獲取items的時候還是兩條SQL語句舵稠,此時我們需要將Item類添加到二級緩存中。

<collection-cache usage="read-write" collection="com.hibernate.n2n.Category.items"></collection-cache>

此時發(fā)現(xiàn)又是四條SQL語句入宦。因為我們僅僅緩存了items集合類哺徊,沒有緩存其中的類型。

<class-cache usage="read-write" class="com.hibernate.n2n.Item"></class-cache>

這個時候控制臺只輸出了兩條SQL語句乾闰。

集合緩存也可以在.hbm.xml中配置

<set name="items" table="category_item">
    <cache usage="read-write"/>
    <key>
        <column name="Category_Id"/>
    </key>
    <many-to-many class="Item" column="Item_Id"/>
</set>

注意落追,還需要在集合包含的類中配置緩存。

ehcache.xml文件

<diskStore path="java.io.tmpdir"/>

diskStore表示磁盤的存儲路徑涯肩。當(dāng)有大量的數(shù)據(jù)的時候我們需要設(shè)置臨界值轿钠,如果低于這個臨界值巢钓,則將數(shù)據(jù)存入到內(nèi)存中。高于這個數(shù)據(jù)則放到磁盤上疗垛。該路徑表示磁盤的位置症汹。

<defaultCache
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    overflowToDisk="true"
/>

默認(rèn)的存儲策略。

<cache name="sampleCache1"
   maxElementsInMemory="10000"
   eternal="false"
   timeToIdleSeconds="300"
   timeToLiveSeconds="600"
   overflowToDisk="true"
/>

設(shè)置具體的命名緩存數(shù)據(jù)的策略继谚,每個命名緩存代表一個緩存區(qū)域烈菌。

緩存區(qū)域(region):一個具有名稱的緩存塊阵幸,可以給每個緩存塊設(shè)置不同的緩存策略花履。如果沒有設(shè)置任何緩存區(qū)域,則所有的緩存對象將使用默認(rèn)的緩存策略挚赊。

配置項解釋:

name:設(shè)置緩存的名字诡壁,它的取值為類的全限定名或類的集合名字。
maxElementInMemory:在內(nèi)存中最多可以存儲對象的數(shù)量荠割。
eternal:設(shè)置對象在緩存中是否為永久的妹卿,true表示永不過期。此時將忽略timeToIdleSeconds和timeToLiveSecond蔑鹦,默認(rèn)值為false夺克。
timeToIdleSeconds:設(shè)置對象空閑的最長時間,單位是秒嚎朽。如果超過這個時間铺纽,對象將從緩沖中清除。
timeToLiveSecond:設(shè)置對象的最長生存時間哟忍,超過這個時間狡门,對象過期。如果值為0表示對象可以永久的存在緩存中锅很。
overflowToDisk:設(shè)置內(nèi)存中的緩存中的對象的數(shù)量達(dá)到上限后其馏,是否把溢出的對象寫到基于硬盤的緩存中。

查詢緩存

當(dāng)執(zhí)行以下的代碼

Query query = session.createQuery("from Category ");
List<Category>categories = query.list();
System.out.println(categories.size());

categories = query.list();
System.out.println(categories.size());

我們會發(fā)現(xiàn)執(zhí)行了兩條SQL爆安。說明緩存沒有起作用叛复。

此時我們需要設(shè)置查詢緩存。

默認(rèn)情況下設(shè)置的緩存對HQL和QBC查詢是無效的扔仓。通過以下方式配置
在Hibernate配置文件中聲明開啟查詢緩存

<property name="cache.use_query_cache">true</property>

調(diào)用Query或Criteria的setCacheable方法褐奥,設(shè)置為true。

query.setCacheable(true);

注意:查詢緩存依賴二級緩存当辐。

時間戳緩存

時間戳緩存區(qū)域存放了對于查詢結(jié)果相關(guān)的表進(jìn)行插入抖僵,更新,刪除操作的時間戳缘揪。Hibernate通過時間戳緩存區(qū)域判斷緩存的查詢結(jié)果是否過期耍群,其運(yùn)行過程如下:

  • T1時刻執(zhí)行查詢操作义桂,把查詢結(jié)果放在QueryCache區(qū)域,記錄該區(qū)域的時間戳為T1蹈垢。
  • T2時刻對查詢結(jié)果相關(guān)的表進(jìn)行更新操作慷吊,Hibernate把T2時刻放在UpdateTimestampCache區(qū)域。
  • T3時刻在執(zhí)行查詢結(jié)果前曹抬,先比較QueryCache區(qū)域的時間戳和UpdateTimestampCache區(qū)域的時間戳溉瓶,若T2>T1,那么丟棄原先存放在QueryCache區(qū)域的查詢結(jié)果谤民,重新懂啊數(shù)據(jù)庫中查詢數(shù)據(jù)堰酿,再把結(jié)果存放到QueryCache區(qū)域;若T2<T1张足,直接從QueryCache區(qū)域取出查詢結(jié)果触创。

Query接口的iterator方法

當(dāng)遍歷訪問結(jié)果集的時候,該方法先到Session緩存及二級緩存中查看是否存在特定OID的對象为牍,如果存在哼绑,就直接返回該對象。如果不存在該對象碉咆,則通過相應(yīng)的SQL select語句到數(shù)據(jù)庫中加載特定的實體類對象抖韩。

大多數(shù)情況下應(yīng)使用list方法執(zhí)行查詢操作。iterator方法僅在滿足以下條件的場景中適合使用疫铜,可以稍微提高查詢的性能:

  • 要查詢的數(shù)據(jù)表中含有大量的字段
  • 啟用了二級緩存茂浮,且二級緩存可能已經(jīng)包含了待查詢的對象。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末块攒,一起剝皮案震驚了整個濱河市励稳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌囱井,老刑警劉巖驹尼,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異庞呕,居然都是意外死亡新翎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門住练,熙熙樓的掌柜王于貴愁眉苦臉地迎上來地啰,“玉大人,你說我怎么就攤上這事讲逛】髁撸” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵盏混,是天一觀的道長蔚鸥。 經(jīng)常有香客問我惜论,道長,這世上最難降的妖魔是什么止喷? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任馆类,我火速辦了婚禮,結(jié)果婚禮上弹谁,老公的妹妹穿的比我還像新娘乾巧。我一直安慰自己,他們只是感情好预愤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布沟于。 她就那樣靜靜地躺著,像睡著了一般鳖粟。 火紅的嫁衣襯著肌膚如雪社裆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天向图,我揣著相機(jī)與錄音,去河邊找鬼标沪。 笑死榄攀,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的金句。 我是一名探鬼主播檩赢,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼违寞!你這毒婦竟也來了贞瞒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤趁曼,失蹤者是張志新(化名)和其女友劉穎军浆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挡闰,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乒融,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了摄悯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赞季。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖奢驯,靈堂內(nèi)的尸體忽然破棺而出申钩,到底是詐尸還是另有隱情,我是刑警寧澤瘪阁,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布撒遣,位于F島的核電站断盛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏愉舔。R本人自食惡果不足惜钢猛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望轩缤。 院中可真熱鬧命迈,春花似錦、人聲如沸火的。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馏鹤。三九已至征椒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間湃累,已是汗流浹背勃救。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留治力,地道東北人蒙秒。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓鳖悠,卻偏偏與公主長得像塞关,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子墩新,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

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