程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Hibernate ——二級緩存,hibernate二級緩存

Hibernate ——二級緩存,hibernate二級緩存

編輯:JAVA綜合教程

Hibernate ——二級緩存,hibernate二級緩存


一、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)查詢緩存依賴於二級緩存

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved