Hibernate學(xué)習(xí)筆記 | 解析二級(jí)緩存

緩存

計(jì)算機(jī)領(lǐng)域非常通用的概念,它介于應(yīng)用程序和永久性數(shù)據(jù)存儲(chǔ)源(如硬盤上的文件或者數(shù)據(jù)庫)之間穿挨,其作用是降低應(yīng)用程序直接讀寫永久性數(shù)據(jù)存儲(chǔ)源的頻率月弛,從而提高應(yīng)用的運(yùn)行性能肴盏,緩存中的數(shù)據(jù)是數(shù)據(jù)存儲(chǔ)源中數(shù)據(jù)的拷貝。
緩存的物理介質(zhì)通常是內(nèi)存帽衙。

Hibernate提供的兩種級(jí)別的緩存:

  • 第一級(jí)別的緩存:Session級(jí)別的緩存
    它是屬于事務(wù)范圍的緩存菜皂,這一級(jí)別的緩存是由Hibernate管理的。
  • 第二級(jí)別的緩存:SessionFactory級(jí)別的緩存
    它是屬于進(jìn)程范圍的緩存厉萝。

SessionFactory級(jí)別的緩存

SessionFactory級(jí)別的緩存可以分為兩類

  • 內(nèi)置緩存
    Hibernate自帶的恍飘,不可卸載的,通常在Hibernate的初始化階段谴垫,Hibernate會(huì)把映射元數(shù)據(jù)和預(yù)定義的SQL語句放到SessionFactory的緩存中章母,映射元數(shù)據(jù)是映射文件中的數(shù)據(jù)(.hbm.xml文件中的數(shù)據(jù))的復(fù)制,該內(nèi)置緩存是只讀的翩剪。

  • 外置緩存(二級(jí)緩存)
    一個(gè)可配置的緩存插件胳施。在默認(rèn)情況下,SessionFactory不會(huì)啟用這個(gè)緩存插件肢专,外置緩存中的數(shù)據(jù)是數(shù)據(jù)庫數(shù)據(jù)的復(fù)制,外置緩存的物理介質(zhì)可以是內(nèi)存或硬盤焦辅。


Hibernate的二級(jí)緩存

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

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

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

  • 經(jīng)常被修改
  • 財(cái)務(wù)數(shù)據(jù),絕對(duì)不允許出現(xiàn)并發(fā)問題
  • 與其他應(yīng)用程序共享的數(shù)據(jù)

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

兩個(gè)并發(fā)的事務(wù)同時(shí)訪問持久層的緩存的相同數(shù)據(jù)時(shí)筷登,也有可能出現(xiàn)各類并發(fā)問題剃根。

二級(jí)緩存可以設(shè)定以下4種類型的并發(fā)訪問策略,每一種訪問策略對(duì)應(yīng)一種事務(wù)隔離級(jí)別

  • 非嚴(yán)格讀寫(Nostrict-read-write)
    不保證緩存與數(shù)據(jù)庫中數(shù)據(jù)的一致性前方,提供Read Uncommited事務(wù)隔離級(jí)別狈醉,對(duì)于極少被修改,而且允許臟讀的數(shù)據(jù)惠险,可以采用這種策略苗傅。
  • 讀寫型(Read-write)
    提供Read Commited數(shù)據(jù)隔離級(jí)別,對(duì)于經(jīng)常讀但是很少被修改的數(shù)據(jù)班巩,可以采用這種隔離類型渣慕。因?yàn)樗梢苑乐古K讀。
  • 事務(wù)型(Transactional)
    僅在受管理環(huán)境下適用抱慌,它提供了Repeatable Read事務(wù)隔離級(jí)別膝蜈,對(duì)于經(jīng)常讀但是很少被修改的數(shù)據(jù)腋粥,揭陽采用這種隔離類型,因?yàn)樗梢苑乐古K讀和不可重復(fù)讀。
  • 只讀型(Read-Only)
    提供了Serializable數(shù)據(jù)隔離級(jí)別肺素,對(duì)于從來不會(huì)被修改的數(shù)據(jù),可以采用這種訪問策略大猛。

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

  • 加入二級(jí)緩存插件的jar包及配置文件
    jar包有:ehcache-2.10.6.jar,hibernate-ehcache-5.4.3.Final.jar,slf4j-api-1.7.25.jar
    配置文件ehcache.xml如下:
<ehcache>
    <diskStore path="java.io.tmpdir"/>

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

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

    <cache name="sampleCache2"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        /> 
</ehcache>

  • 配置Hibernate.cfg.cml文件
    配置啟用hibernate的二級(jí)緩存:<property name="cache.use_second_level_cache">true</property>
    配置Hibernate二級(jí)緩存使用的產(chǎn)品:<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhcacheRegionFactory</property>
    配置對(duì)哪些類使用Hibernate的二級(jí)緩存:<class-cache class="com.cerr.hibernate.entities.Employee" usage="read-write" />

實(shí)際上也可以在.hbm.xml文件中配置對(duì)哪些類使用二級(jí)緩存及二級(jí)緩存的策略兰迫。例如在Employee.hbm.xml文件的class節(jié)點(diǎn)里面加上<cache usage="read-write"/>

設(shè)置二級(jí)緩存之后码秉,測(cè)試如下:

@org.junit.Test
    public void testHibernateSecondLevelCache(){
        Employee employee = session.get(Employee.class,1);
        System.out.println(employee.getName());
        //關(guān)閉session后重新連接
        transaction.commit();
        session.close();
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        Employee employee1 = session.get(Employee.class,1);
        System.out.println(employee1.getName());

    }

在沒設(shè)置二級(jí)緩存的時(shí)候逮矛,該情形會(huì)發(fā)送兩次sql語句,因?yàn)樵谥虚g關(guān)閉了session后再重新開啟转砖。而我們已經(jīng)設(shè)置了二級(jí)緩存须鼎,因此只會(huì)發(fā)送一條sql語句。

集合級(jí)別的二級(jí)緩存的配置

  • 配置對(duì)集合使用二級(jí)緩存府蔗,例如:<collection-cache collection="com.cerr.hibernate.entities.Department.emps" usage="read-write" />晋控,也可以在hbm.xml文件中使用<cache>標(biāo)簽進(jìn)行配置。
  • 還需要配置集合中的元素對(duì)應(yīng)的持久化類也使用二級(jí)緩存姓赤,否則將會(huì)多出n條SQL語句赡译。

以Department為例,配置文件hibernate.cfg.xml如下:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- 配置連接數(shù)據(jù)庫的基本信息 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hibernate5</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>

        <!-- 配置hibernate的基本信息-->
        <!-- hibernate所使用的的數(shù)據(jù)庫方言 -->
        <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <!-- 執(zhí)行操作時(shí)是否在控制臺(tái)打印SQL-->
        <property name="show_sql">true</property>
        <!-- 是否對(duì)SQL進(jìn)行格式化-->
        <property name="format_sql">true</property>
        <!-- 指定生成數(shù)據(jù)表的策略-->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- 設(shè)置hibernate的事務(wù)隔離級(jí)別 -->
        <property name="connection.isolation">2</property>
        <property name="use_identifier_rollback">true</property>

        <!-- 配置C3P0數(shù)據(jù)源 -->
        <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
        <property name="c3p0.max_size">10</property>
        <property name="c3p0.min_size">5</property>
        <property name="c3p0.acquire_increment">2</property>
        <property name="c3p0.idle_test_period">2000</property>
        <property name="c3p0.timeout">2000</property>
        <property name="c3p0.max_statements">10</property>

        <!-- 設(shè)定JDBC的Statement讀取數(shù)據(jù)的時(shí)候每次從數(shù)據(jù)庫中取出的記錄條數(shù)-->
        <property name="hibernate.jdbc.fetch_size">100</property>
        <!-- 設(shè)定對(duì)數(shù)據(jù)庫進(jìn)行批量刪除不铆,更新蝌焚,插入的時(shí)候批次的大小 -->
        <property name="hibernate.jdbc.batch_size">30</property>


        <!-- 啟用二級(jí)緩存-->
        <property name="cache.use_second_level_cache">true</property>
        <!-- 配置使用的二級(jí)緩存的產(chǎn)品 -->
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhcacheRegionFactory</property>

        <mapping resource="com/cerr/hibernate/entities/Employee.hbm.xml"/>
        <mapping resource="com/cerr/hibernate/entities/Department.hbm.xml"/>

        <class-cache class="com.cerr.hibernate.entities.Employee" usage="read-write" />
        <class-cache class="com.cerr.hibernate.entities.Department" usage="read-write" />
        <!-- 對(duì)Department的集合使用二級(jí)緩存-->
        <collection-cache collection="com.cerr.hibernate.entities.Department.emps" usage="read-write" />
    </session-factory>
</hibernate-configuration>

測(cè)試:

@org.junit.Test
    public void testCollectionSecondLevelCache(){
        Department department = session.get(Department.class,1);
        System.out.println(department.getName());
        System.out.println(department.getEmps().size());
        transaction.commit();
        session.close();
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Department department1 = session.get(Department.class,1);
        System.out.println(department1.getName());
        System.out.println(department1.getEmps().size());

    }

對(duì)ehcache.xml文件的解析

<ehcache>
    <!-- 指定一個(gè)目錄:當(dāng)EHCache把數(shù)據(jù)寫到硬盤上時(shí),將把數(shù)據(jù)寫到這個(gè)目錄下 -->
    <diskStore path="java.io.tmpdir"/>

    <!-- 設(shè)置緩存的默認(rèn)數(shù)據(jù)過期策略 -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />
    <!-- 設(shè)置具體的命名緩存的數(shù)據(jù)過期策略誓斥,每個(gè)命名緩存代表一個(gè)緩存區(qū)域-->
    <cache name="com.cerr.hibernate.entities.Employee"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <cache name="com.cerr.hibernate.entities.Department"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        />
</ehcache>

關(guān)于幾個(gè)標(biāo)簽

  • <diskStore>標(biāo)簽
    指定一個(gè)目錄:當(dāng)EHCache把數(shù)據(jù)寫到硬盤上時(shí)只洒,將把數(shù)據(jù)寫到這個(gè)目錄下
  • <defaultCache>標(biāo)簽
    設(shè)置緩存的默認(rèn)數(shù)據(jù)過期策略
  • <cache>標(biāo)簽
    設(shè)置具體的命名緩存的數(shù)據(jù)過期策略,每個(gè)命名緩存代表一個(gè)緩存區(qū)域

Hibernate在不同的緩存區(qū)域保存不同的類/集合
對(duì)于類而言劳坑,區(qū)域的名稱是類名毕谴,例如:com.cerr.domain.Customer
對(duì)于集合而言,區(qū)域的名稱是類名加屬性名距芬,例如:com.cerr.domain.Customer.orders

cache元素的屬性

  • name:設(shè)置緩存的名字涝开,它的取值為類的全限定名或類的集合的名字。
  • maxInMemory:設(shè)置基于內(nèi)存的緩存中可存放的對(duì)象的最大數(shù)目框仔。
  • etemal:設(shè)置對(duì)象是否為永久的舀武,true表示永不過期,此時(shí)將忽略timeToldleSecondstimeToLiveSeconds屬性:默認(rèn)是false离斩。
  • timeToldleSeconds:設(shè)置對(duì)象空閑最長時(shí)間奕剃,以秒為單位,超過這個(gè)時(shí)間捐腿,對(duì)象過期纵朋。當(dāng)對(duì)象過期時(shí),EHCache會(huì)把它從緩存中清除茄袖。如果此值為0操软,表示對(duì)象可以無限期地處于空閑狀態(tài)。
  • timeToLiveSeconds:設(shè)置對(duì)象生存最長時(shí)間宪祥,超過這個(gè)時(shí)間聂薪,對(duì)象過期家乘。如果此值為0,表示對(duì)象可以無限期地存在于緩存中藏澳,該屬性值必須大于或等于timeToldleSeconds屬性值仁锯。
  • overflowToDisk:設(shè)置基于內(nèi)在的緩存中的對(duì)象數(shù)目達(dá)到上限后,是否把溢出的對(duì)象寫在基于硬盤的緩存中翔悠。

查詢緩存

默認(rèn)情況下业崖,設(shè)置的緩存對(duì)HQL及QBC查詢是無效的,但可以通過設(shè)置查詢緩存來使其支持這兩種查詢蓄愁。

設(shè)置的步驟:

  • hibernate.cfg.xml文件中聲明開啟查詢緩存双炕,例如:<property name="cache.use_query_cache">true</property>
  • 調(diào)用QueryCriteriasetCacheable(true)

注意:查詢緩存依賴于二級(jí)緩存撮抓,即若要使用查詢緩存妇斤,必須先配置二級(jí)緩存,否則無法使用丹拯。

Demo如下:

@org.junit.Test
    public void testQueryCache(){
        Query query = session.createQuery("FROM Employee ");
        //設(shè)置查詢緩存
        query.setCacheable(true);

        List<Employee> employees = query.list();
        System.out.println(employees.size());

        employees = query.list();
        System.out.println(employees.size());
    }

時(shí)間戳緩存區(qū)域

時(shí)間戳緩存區(qū)域存放了對(duì)于查詢結(jié)果相關(guān)的表進(jìn)行插入站超,更新或刪除操作的時(shí)間戳,Hibernate通過時(shí)間戳緩存區(qū)域來判斷被緩存的查詢結(jié)果是否過期乖酬,其運(yùn)行過程如下:

  • T1時(shí)刻執(zhí)行查詢操作死相,把查詢結(jié)果存放在QueryCache區(qū)域,記錄該區(qū)域的時(shí)間戳為T1.
  • T2時(shí)刻對(duì)查詢結(jié)果相關(guān)的表進(jìn)行更新操作剑刑,Hibernate把T2時(shí)刻存放在UpdateTimestampCache區(qū)域。
  • T3時(shí)刻執(zhí)行查詢結(jié)果前双肤,先比較QueryCache區(qū)域的時(shí)間戳和UpdateTimestampCache區(qū)域的時(shí)間戳施掏,若T2>T1,那么就丟棄原先存放在QueryCache區(qū)域的查詢結(jié)果茅糜,重新到數(shù)據(jù)庫中查詢數(shù)據(jù)七芭,再把結(jié)果存放到QueryCache區(qū)域;若T2<T1蔑赘,直接從QueryCache中獲得查詢結(jié)果狸驳。

Query接口的iterate()方法

list()一樣也能執(zhí)行查詢操作。
list()執(zhí)行的SQL語句包含實(shí)體類對(duì)應(yīng)的數(shù)據(jù)表的所有字段缩赛。
iterator()執(zhí)行的SQL語句中僅包含實(shí)體類對(duì)應(yīng)的數(shù)據(jù)表的ID字段耙箍。
當(dāng)遍歷訪問結(jié)果集時(shí),該方法先到Session緩存及二級(jí)緩存中查看是否存在特定OID的對(duì)象酥馍,如果存在辩昆,就直接返回該對(duì)象,如果不存在該對(duì)象就通過相應(yīng)的SQL Select語句到數(shù)據(jù)庫中加載特定的實(shí)體對(duì)象

大多數(shù)情況下旨袒,應(yīng)考慮使用list()執(zhí)行查詢操作汁针,iterator()僅僅在滿足以下條件的場(chǎng)合术辐,可以稍微提高查詢性能:

  • 要查詢的數(shù)據(jù)表中包含大量字段
  • 啟用了二級(jí)緩存,且二級(jí)緩存中可能已經(jīng)包含了待查詢的對(duì)象施无。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辉词,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子猾骡,更是在濱河造成了極大的恐慌瑞躺,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卓练,死亡現(xiàn)場(chǎng)離奇詭異隘蝎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)襟企,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門嘱么,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人顽悼,你說我怎么就攤上這事曼振。” “怎么了蔚龙?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵冰评,是天一觀的道長。 經(jīng)常有香客問我木羹,道長甲雅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任坑填,我火速辦了婚禮抛人,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脐瑰。我一直安慰自己妖枚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布苍在。 她就那樣靜靜地躺著绝页,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寂恬。 梳的紋絲不亂的頭發(fā)上续誉,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音初肉,去河邊找鬼屈芜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的井佑。 我是一名探鬼主播属铁,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼躬翁!你這毒婦竟也來了焦蘑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤盒发,失蹤者是張志新(化名)和其女友劉穎例嘱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宁舰,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拼卵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蛮艰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腋腮。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖壤蚜,靈堂內(nèi)的尸體忽然破棺而出即寡,到底是詐尸還是另有隱情,我是刑警寧澤袜刷,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布聪富,位于F島的核電站,受9級(jí)特大地震影響著蟹,放射性物質(zhì)發(fā)生泄漏墩蔓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一萧豆、第九天 我趴在偏房一處隱蔽的房頂上張望奸披。 院中可真熱鬧,春花似錦炕横、人聲如沸源内。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嗽交,卻和暖如春卿嘲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背夫壁。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工拾枣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓梅肤,卻偏偏與公主長得像司蔬,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子姨蝴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

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

  • 一俊啼、Hibernate 緩存 緩存(Cache): 計(jì)算機(jī)領(lǐng)域非常通用的概念。它介于應(yīng)用程序和永久性數(shù)據(jù)存儲(chǔ)源(如...
    leeqico閱讀 502評(píng)論 0 0
  • Hibernate是一個(gè)開放源代碼的對(duì)象關(guān)系映射框架左医,它對(duì)JDBC進(jìn)行了非常輕量級(jí)的對(duì)象封裝授帕,它將POJO與數(shù)據(jù)庫...
    蘭緣小妖閱讀 1,208評(píng)論 1 18
  • 1.JVM 堆內(nèi)存和非堆內(nèi)存 堆和非堆內(nèi)存按照官方的說法:“Java 虛擬機(jī)具有一個(gè)堆(Heap),堆是運(yùn)行時(shí)數(shù)據(jù)...
    yanzhu728閱讀 915評(píng)論 0 0
  • Hibernate 二級(jí)緩存 Hibernate 二級(jí)緩存 總結(jié)整理 可以在進(jìn)程或集群的級(jí)別上為事務(wù)之間可重用的數(shù)...
    WesleyLien閱讀 394評(píng)論 0 0
  • 鄧茜(泉州九中初二)原創(chuàng) 按兵不動(dòng) 黯景愁庭月盛情浮梢, 冰寒假樹翠竹亭跛十。 不征萬戰(zhàn)東風(fēng)沁, 凍亂蒼蔑跸酰苦墜盈芥映。 鄧茜 14
    慧益大師閱讀 110評(píng)論 0 1