Hibernate ——二級緩存

来源:http://www.cnblogs.com/solverpeng/archive/2016/10/14/5961935.html
-Advertisement-
Play Games

一、Hibernate 二級緩存 1.Hibernate 二級緩存是 SessionFactory 級別的緩存。 2.二級緩存分為兩類: (1)Hibernate內置二級緩存 (2)外置緩存,可配置的,可插撥的,外置緩存中的數據是資料庫數據的複製。 3.二級緩存的併發訪問策略 (1)兩個併發的事務同 ...


一、Hibernate 二級緩存

1.Hibernate 二級緩存是 SessionFactory 級別的緩存。

2.二級緩存分為兩類:

(1)Hibernate內置二級緩存

(2)外置緩存,可配置的,可插撥的,外置緩存中的數據是資料庫數據的複製。

3.二級緩存的併發訪問策略

(1)兩個併發的事務同時訪問持久層的緩存的相同數據時,也有可能出現併發問題。

(2)二級緩存可以設定以下 4 中併發訪問策略,每一種對應一種事務隔離級別。

  • 非嚴格讀寫(Nonstrict-read-write):不保證緩存與資料庫中數據的一致性。對應 Read UnCommited 事務隔離級別。
  • 讀寫型(Read-write):提供 Read Commited 級別的事務隔離級別。
  • 事務型(Transactional):對應 Repeatable Read 級別的事務隔離級別。
  • 只讀型(Read-Only):提供 Serializable 級別的數據隔離級別。

4.Hibernate 二級緩存是進程或集群範圍內的緩存。是可配置的的插件。這裡以 Ehcache 為例。不支持事務型的併發訪問策略。

5.配置 Ehcache

(1)添加 Jar 包

(2)在 Hibernate 配置文件中啟用二級緩存並指定適配的緩存適配器。

在 <session-factory> 元素內添加:

<property name="cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

(3)在 Hibernate 配置文件中配置需要使用二級緩存的持久化類,並設置它的二級緩存的併發訪問策略。如:

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>

需要註意的是:<session-factory> 元素內子元素的順序:property*, mapping*, (class-cache|collection-cache)*, event*, listener*,class-cache 元素必須位於 mapping 節點後。

(4)或者也可以在 hbm 文件中配置,如:

<hibernate-mapping>

    <class name="com.solverpeng.hql.Employee" table="employee" schema="hibernate">
        <cache usage="read-write"/>
        <id name="empId" column="emp_id"/>
        <property name="empName" column="emp_name"/>
        <property name="salary" column="salary"/>
        <many-to-one name="dept" class="com.solverpeng.hql.Department">
            <column name="dept_id_fk" not-null="true"/>
        </many-to-one>
    </class>
</hibernate-mapping>

6. 測試 Hibernate 二級緩存

(1)查詢單個對象緩存

@Test
public void testSecondCache() {
    Employee o = (Employee) session.get(Employee.class, 6);
    System.out.println(o);

    transaction.commit();
    session.close();
    session = factory.openSession();
    transaction = session.beginTransaction();

    Employee o1 = (Employee) session.get(Employee.class, 6);
    System.out.println(o1);
}

查詢同一個對象兩次,中間關閉 Session 再開啟。

沒有配置二級緩存:

Hibernate: 
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee employee0_ 
    where
        employee0_.emp_id=?
com.solverpeng.hql.Employee@40dd58f3
Hibernate: 
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee employee0_ 
    where
        employee0_.emp_id=?
com.solverpeng.hql.Employee@40dd58f3

結果:

發送了2條 SQL 語句。

配置二級緩存後(可以在Hibernate 配置文件中配置,也可以在 Employee hbm 配置文件中配置):

Hibernate: 
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee employee0_ 
    where
        employee0_.emp_id=?
com.solverpeng.hql.Employee@40dd58f3
com.solverpeng.hql.Employee@40dd58f3

結果:

只發送了1條 SQL 語句。

(2)集合緩存

@Test
public void testCollectionSecondLevelCache() {
    Department department = (Department) session.get(Department.class, 6);
    System.out.println(department.getDeptName());
    System.out.println(department.getEmps().size());

    transaction.commit();
    session.close();
    session = factory.openSession();
    transaction = session.beginTransaction();

    Department department2 = (Department) session.get(Department.class, 6);
    System.out.println(department2.getDeptName());
    System.out.println(department2.getEmps().size());

}

在沒有配置二級緩存的情況下:

Hibernate: 
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_ 
    from
        hibernate.department department0_ 
    where
        department0_.dept_id=?
dept-aa
Hibernate: 
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee emps0_ 
    where
        emps0_.dept_id_fk=?
3
Hibernate: 
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_ 
    from
        hibernate.department department0_ 
    where
        department0_.dept_id=?
dept-aa
Hibernate: 
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee emps0_ 
    where
        emps0_.dept_id_fk=?
3

結果:

查詢了兩次 Department,兩次Employee

只配置 Department 二級緩存:

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>
Hibernate: 
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_ 
    from
        hibernate.department department0_ 
    where
        department0_.dept_id=?
dept-aa
Hibernate: 
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee emps0_ 
    where
        emps0_.dept_id_fk=?
3
dept-aa
Hibernate: 
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee emps0_ 
    where
        emps0_.dept_id_fk=?
3

結果:

只查詢了一次 Department,2次Employee。

說明:

開啟 Department 二級緩存後,會對 Department 進行緩存,而與其關聯的 emps 不會進行緩存。

配置 Department 二級緩存,同時配置關聯的 emps 緩存。

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>
<collection-cache collection="com.solverpeng.hql.Department.emps" usage="read-write"/>
Hibernate: 
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_ 
    from
        hibernate.department department0_ 
    where
        department0_.dept_id=?
dept-aa
Hibernate: 
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee emps0_ 
    where
        emps0_.dept_id_fk=?
3
-----------------------------
dept-aa
Hibernate: 
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee employee0_ 
    where
        employee0_.emp_id=?
Hibernate: 
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee employee0_ 
    where
        employee0_.emp_id=?
Hibernate: 
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee employee0_ 
    where
        employee0_.emp_id=?
3

結果:

發送了更多的查詢 Employee 的SQL。

說明:

開啟集合的二級緩存後,此時會緩存集合中對象的 id ,而不會對集合中的對象進行緩存。若想緩存,需要關聯的集合中的對象也開啟二級緩存。如:

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>
<collection-cache collection="com.solverpeng.hql.Department.emps" usage="read-write"/>
<class-cache class="com.solverpeng.hql.Employee" usage="read-write"/>
Hibernate: 
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_ 
    from
        hibernate.department department0_ 
    where
        department0_.dept_id=?
dept-aa
Hibernate: 
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_ 
    from
        hibernate.employee emps0_ 
    where
        emps0_.dept_id_fk=?
3
-----------------------------
dept-aa
3

結果:

除 Department 外,關聯的 Employee 也被緩存了。

(3)查詢緩存(針對 HQL、QBC)

在二級緩存開啟的情況下,HQL、QBC 也不能對查詢進行緩存。

@Test
public void testQueryCache() {
    Query query = session.createQuery("from Employee ");

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

    List<Employee> emps2 = query.list();
    System.out.println(emps2.size());
}
Hibernate: 
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_ 
    from
        hibernate.employee employee0_
12
Hibernate: 
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_ 
    from
        hibernate.employee employee0_
12

開啟查詢緩存:

  • 在 Hibernate 配置文件中開啟查詢緩存
  • 若想啟用查詢緩存的查詢語句,需要調用 Query 或 Criteria 的 setCacheable() 方法。
  • 查詢緩存依賴於二級緩存
<property name="hibernate.cache.use_query_cache">true</property>
@Test
public void testQueryCache() {
    Query query = session.createQuery("from Employee ");
    query.setCacheable(true);
    List<Employee> list = query.list();
    System.out.println(list.size());

    List<Employee> emps2 = query.list();
    System.out.println(emps2.size());
}
Hibernate: 
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_ 
    from
        hibernate.employee employee0_
12
12

(4)時間戳緩存區域:時間戳緩存區存放了對於查詢結果相關的表進行插入、更新或刪除操作的時間戳。Hibernate通過時間戳緩存區來判定被緩存的查詢結果是否過期。

@Test
public void testTimStampCache() {
    Query query = session.createQuery("from Employee ");
    query.setCacheable(true);
    List<Employee> list = query.list();
    System.out.println(list.size());

    Employee employee = (Employee) session.get(Employee.class, 6);
    employee.setEmpName("emp@@");

    List<Employee> emps2 = query.list();
    System.out.println(emps2.size());
}
Hibernate: 
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_ 
    from
        hibernate.employee employee0_
12
Hibernate: 
    update
        hibernate.employee 
    set
        emp_name=?,
        salary=?,
        dept_id_fk=? 
    where
        emp_id=?
Hibernate: 
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_ 
    from
        hibernate.employee employee0_
12

二、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>

1.<diskStore>: 指定一個目錄:當 EHCache 把數據寫到硬碟上時, 將把數據寫到這個目錄下.

2.<defaultCache>: 設置緩存的預設數據過期策略

3.<cache> 設定具體的命名緩存的數據過期策略。每個命名緩存代表一個緩存區域

4.緩存區域(region):一個具有名稱的緩存塊,可以給每一個緩存塊設置不同的緩存策略。如果沒有設置任何的緩存區域,則所有被緩存的對象,都將使用預設的緩存策略。即:<defaultCache.../>

5.Hibernate在不同的緩存區域保存不同的類/集合。

  • 對於類而言,區域的名稱是類名。如:com.atguigu.domain.Customer
  • 對於集合而言,區域的名稱是類名加屬性名。如com.atguigu.domain.Customer.orders

6.cache 元素的屬性

(1)name:設置緩存的名字,它的取值為類的全限定名或類的集合的名字

(2)maxInMemory:設置基於記憶體的緩存中可存放的對象最大數目

(3)eternal:設置對象是否為永久的,true表示永不過期,此時將忽略timeToIdleSeconds 和 timeToLiveSeconds屬性; 預設值是false。

(4)timeToIdleSeconds:設置對象空閑最長時間,以秒為單位, 超過這個時間,對象過期。當對象過期時,EHCache會把它從緩存中清除。如果此值為0,表示對象可以無限期地處於空閑狀態。

(5)timeToLiveSeconds:設置對象生存最長時間,超過這個時間,對象過期。如果此值為0,表示對象可以無限期地存在於緩存中. 該屬性值必須大於或等於 timeToIdleSeconds 屬性值。

(6)overflowToDisk:設置基於記憶體的緩存中的對象數目達到上限後,是否把溢出的對象寫到基於硬碟的緩存中。

三、管理 Session

Session 對象的生命周期與本地線程綁定

–Session 對象的生命周期與 JTA 事務綁定

–Hibernate 委托程式管理 Session 對象的生命周期

四、批量處理數據

建議通過 JDBC 的方式來進行批量操作

@Test
public void testBathch() {
    session.doWork(new Work() {
        @Override
        public void execute(Connection connection) throws SQLException {
            // 執行批量操作
        }
    });
}

五、總結

配置 Hibernate 二級緩存的步驟:

1.配置 Hibernate 配置文件

(1)配置啟用二級緩存

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

(2)配置二級緩存使用的產品

<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

(3)配置對哪些類使用 hibernate 的二級緩存以及併發策略

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>
<collection-cache collection="com.solverpeng.hql.Department.emps" usage="read-write"/>
<class-cache class="com.solverpeng.hql.Employee" usage="read-write"/>

(4)在 hbm 文件中配置緩存

<set name="emps" inverse="true">
    <cache usage="read-write"/>
    <key>
        <column name="dept_id_fk" not-null="true"/>
    </key>
    <one-to-many not-found="ignore" class="com.solverpeng.hql.Employee"/>
</set>

2. 對於集合緩存來說,還需要配置集合中的元素對應的持久化類也使用二級緩存! 否則將會多出 n 條 SQL 語句.

3. 查詢緩存

(1)在 Hibernate 配置文件中開啟查詢緩存支持:<property name="cache.use_query_cache">true</property>

(2)調用 Query 或 Criteria 的 setCacheable(true)方法

(3)查詢緩存依賴於二級緩存

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、Python的介紹 python的創始人為吉多.範羅蘇姆。1989年的聖誕期間,吉多.範羅蘇姆為了在阿姆斯特丹打發時間,決心開發一個新的腳本解釋程式,作為ABC語言的一種繼承。 二、Python是一門什麼樣的語言? 編程語言主要是從以下幾個角度進行分類,編譯型和解釋型、靜態語言和動態語言、強類型 ...
  • 一直以來把資料庫的表轉換成Entity或DTO都是一件讓人頭痛的事情,既浪費時間又很繁瑣,用其他工具生成多少會有一些不盡人意的地方,於是就自己用Swing寫了一個通過資料庫的表生成JavaBean的工具,支持MySQL、Oracle、SQLServce、PostgreSQL,完美支持JPA註解,可以... ...
  • 線程間的通信:多個線程在處理同一資源,但是任務卻不同。一、等待喚醒機制涉及的方法:1.wait();讓線程處於凍結狀態,被wait的線程會被存儲到線程池中2.notify();喚醒線程池中的一個任意線程3.notifyAll();喚醒線程池中的所有線程這些方法都必須定義在同步中,因為這些方法是用於操 ...
  • 字元: 字元是可使用多種不同字元方案或代碼頁來表示的抽象實體。例如,Unicode UTF-16 編碼將字元表示為 16 位整數序列,而 Unicode UTF-8 編碼則將相同的字元表示為 8 位位元組序列。公共語言運行庫使用 Unicode UTF-16(Unicode 轉換格式,16 位編碼形式 ...
  • 最近在做學校的課程設計,java編程需要用到對話框彈出,第一反應是js中的alert和confirm,java的話瞬間懵,查閱學習總結如下,用以以後的學習 1.顯示一個錯誤對話框,該對話框顯示的 message 為 'alert':JOptionPane.showMessageDialog(null ...
  • *************************hibernate.cfg.xml <?xml version='1.0' encoding='utf-8'?><!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Confi ...
  • 之前是一直做asp.net開發的,Visual Studio 2012 用起來感覺是相當的舒服,調試功能也是相當好、相當智能,大大的提高了開發效率減少了程 序BUG的出現,而且直接安裝好就能用了,無需多餘的操作,非常方便。 由於這次要做PHP開發,為了搭建開發環境也是搜索了不少資料,選擇IDE也是參 ...
  • 雖然目前Java算不上前端開發的主力,但是作為Java入門基礎的一部分,學習Java的GUI編程還是有必要的,而且可以做出一些小且有趣的圖形程式來提高學習熱情。本篇學習總結均為一個Beginner的筆記與心得,如有描述不到或錯誤之處,敬請指正。 一個合格的Java Developer,不僅要掌握技術... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...