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>
  1. hbm文件中添加緩存使用情況設置
<cache usage="read-only" />
  1. 創建ehcache.xml文件
<?xml version="1.0"?>  
<ehcache>  
    <defaultCache maxElementsInMemory="100"  eternal="true"/>  
</ehcache>

Hibernate二級緩存示例

要通過下面示例瞭解二級緩存,我們需要創建一個Java項目:secondlevel, 其完整的目錄結構如下所示 -
Hibernate二級緩存

創建以下頁面:

  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