Hibernate二級快取


Hibernate第二級快取是對談工廠的所有對談(Session)物件所使用的公共快取。 如果您有來自對談工廠的多個對談(Session)物件,就可以操作對談工廠中的第二級快取的資料。

SessionFactory類用於儲存二級快取資料。 它是所有對談物件的全域性,預設情況下是不啟用的。

不同廠商提供了二級快取的實現。

  1. EH二級快取
  2. OS二級快取
  3. Swarm二級快取
  4. JBoss二級快取

每個實現提供不同的快取使用功能。 有四種方法可以使用二級快取。

  1. 唯讀:快取將適用於唯讀操作。
  2. 非嚴格讀寫:快取可用於讀寫,但一次只能讀寫。
  3. 讀寫:快取將用於讀寫,可以同時使用。
  4. 事務處理:快取將用於事務處理。
    快取使用屬性可以應用於hbm.xml檔案中的類或集合級別。 下面給出了定義快取使用情況的例子:
    <cache usage="read-only" />
    
    下面來看看看二級快取實現和快取使用情況。
實現 唯讀 非限制讀寫 讀寫 操作
EH二級快取 Yes Yes Yes No
OS二級快取 Yes Yes Yes No
Swarm二級快取 Yes Yes No No
JBoss二級快取 No No No Yes

使用EH快取的二級快取範例的額外步驟

1)在hibernate.cfg.xml檔案中新增2個組態設定

<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>  
<property name="hibernate.cache.use_second_level_cache">true</property>

2) 在hbm檔案中新增快取使用情況設定

<cache usage="read-only" />

3) 建立ehcache.xml檔案

<?xml version="1.0"?>  
<ehcache>  
    <defaultCache maxElementsInMemory="100"  eternal="true"/>  
</ehcache>

Hibernate二級快取範例

要通過下面範例了解二級快取,我們需要建立一個Java專案:secondlevel, 其完整的目錄結構如下所示 -

建立以下頁面:

  1. Employee.java
  2. employee.hbm.xml
  3. hibernate.cfg.xml
  4. ehcache.xml
  5. MainTest.java

注意:在這裡,我們假設,在MySQL資料庫中有一個emp_cache2表並包含一些記錄。

檔案: Employee.java

package com.yiibai;

public class Employee {
    private int id;
    private String name;
    private float salary;

    public Employee() {
        super();
    }

    public Employee(String name, float salary) {
        super();
        this.name = name;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getSalary() {
        return salary;
    }

    public void setSalary(float salary) {
        this.salary = salary;
    }

}

檔案: employee.hbm.xml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
          "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.yiibai.Employee" table="emp_cache">
        <cache usage="read-only" />
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>
        <property name="salary"></property>
    </class>

</hibernate-mapping>

在這裡,我們使用唯讀(read-only)快取記憶體來使用該類。快取使用情況也可用於集合。

檔案: hibernate.cfg.xml



要實現二級快取,我們需要在組態檔案中定義cache.provider_class屬性。

檔案:ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
    monitoring="autodetect" dynamicConfig="true">

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

    <defaultCache maxEntriesLocalHeap="10000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
        maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU" statistics="true">
        <persistence strategy="localTempSwap" />
    </defaultCache>

    <cache name="org.hibernate.cache.internal.StandardQueryCache"
        maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
        <persistence strategy="localTempSwap" />
    </cache>

    <cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
        maxEntriesLocalHeap="5000" eternal="true">
        <persistence strategy="localTempSwap" />
    </cache>
    <cache name="com.yiibai.Employee" maxElementsInMemory="100"
        eternal="false" timeToIdleSeconds="5" timeToLiveSeconds="200" />
</ehcache>

我們需要建立ehcache.xml檔案來定義快取屬性。

defaultCache將用於所有持久化類。 我們還可以通過使用 cache 元素來明確定義持久化類。
eternal 如果我們指定eternal =「true」,則不需要定義timeToIdleSecondstimeToLiveSeconds屬性,因為它將由hibernate內部處理。 指定eternal =「false」給程式員控制,但是我們需要定義timeToIdleSecondstimeToLiveSeconds屬性
timeToIdleSeconds它定義了二級快取中物件可以空閒多少秒。
timeToLiveSeconds它定義了在第二級快取中物件可以儲存多少秒,無論它是否空閒。

檔案:MainTest.java

package com.yiibai;

import org.hibernate.*;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.*;
import org.hibernate.stat.Statistics;

public class MainTest {
    public static void main(String[] args) {
        // 在5.1.0版本匯總,hibernate則採用如下新方式獲取:
        // 1. 組態型別安全的準服務註冊類,這是當前應用的單例物件,不作修改,所以宣告為final
        // 在configure("cfg/hibernate.cfg.xml")方法中,如果不指定資源路徑,預設在類路徑下尋找名為hibernate.cfg.xml的檔案
        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure("hibernate.cfg.xml").build();
        // 2. 根據服務註冊類建立一個後設資料資源集,同時構建後設資料並生成應用一般唯一的的session工廠
        SessionFactory sessionFactory = new MetadataSources(registry)
                .buildMetadata().buildSessionFactory();

        /**** 上面是組態準備,下面開始我們的資料庫操作 ******/
        Session session = sessionFactory.openSession();// 從對談工廠獲取一個session

        // creating transaction object
        Transaction tx = session.beginTransaction();

        Statistics stats = sessionFactory.getStatistics();
        System.out.println("Stats enabled="+stats.isStatisticsEnabled());

        stats.setStatisticsEnabled(true);
        System.out.println("Stats enabled="+stats.isStatisticsEnabled());

        session.save(new Employee("蘇小牛", 12000));
        session.save(new Employee("庫日天", 19000));

        Session session1 = sessionFactory.openSession();
        Employee emp1 = (Employee) session1.load(Employee.class, 1);
        System.out.println(emp1.getId() + " " + emp1.getName() + " "
                + emp1.getSalary());
        session1.close();

        //再次查詢ID=1的員工資訊,因為使用了快取,這裡不會再發出查詢語句...
        Session session11 = sessionFactory.openSession();
        Employee emp11 = (Employee) session11.load(Employee.class, 1);
        System.out.println(emp11.getId() + " " + emp11.getName() + " "
                + emp11.getSalary());
        session11.close();


        Session session2 = sessionFactory.openSession();
        Employee emp2 = (Employee) session2.load(Employee.class, 2);
        System.out.println(emp2.getId() + " " + emp2.getName() + " "
                + emp2.getSalary());
        session2.close();

        tx.commit();
        session.close();
        sessionFactory.close();
    }
}

我們可以看到,hibernate不會發出兩次查詢。 如果不使用二級快取,hibernate將會發出兩次查詢,因為這兩個查詢都使用不同的對談物件。

執行範例

首先執行 MainTest.java 插入資料,然後再讀取資料。
執行 MainTest.java 得到以下結果,

log4j:WARN No appenders could be found for logger (org.jboss.logging).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Wed Mar 29 21:38:06 CST 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Stats enabled=false
Stats enabled=true
Hibernate: insert into emp_cache (name, salary) values (?, ?)
Hibernate: insert into emp_cache (name, salary) values (?, ?)
Hibernate: select employee0_.id as id1_0_0_, employee0_.name as name2_0_0_, employee0_.salary as salary3_0_0_ from emp_cache employee0_ where employee0_.id=?
Wed Mar 29 21:38:08 CST 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
1 蘇小牛 12000.0
1 蘇小牛 12000.0
Hibernate: select employee0_.id as id1_0_0_, employee0_.name as name2_0_0_, employee0_.salary as salary3_0_0_ from emp_cache employee0_ where employee0_.id=?
2 庫日天 19000.0