天天看點

Hibernate緩存

Hibernate緩存

  緩存是介于實體資料源與應用程式之間,是對資料庫中的資料複制一份臨時放在記憶體中的容器,其作用是為了減少應用程式對實體資料源通路的次數,進而提高了應用的運作性能。Hibernate在進行讀取資料的時候,根據緩存機制在相應的緩存中查詢,如果在緩存中找到了需要的資料(我們把這稱做“緩存命中"),則就直接把命中的資料作為結果加以利用,避免了大量發送SQL語句到資料庫查詢的性能損耗。

Hibernate緩存分類:

一、Session緩存(又稱作事務緩存):Hibernate内置的,不能卸除。

緩存範圍:緩存隻能被目前Session對象通路。緩存的生命周期依賴于Session的生命周期,當Session被關閉後,緩存也就結束生命周期。

二、SessionFactory緩存(又稱作應用緩存):使用第三方插件,可插拔。

緩存範圍:緩存被應用範圍内的所有session共享。這些session有可能是并發通路緩存,是以必須對緩存進行更新。緩存的生命周期依賴于應用的生命周期,應用結束時,緩存也就結束了生命周期,二級緩存存在于應用程式範圍。

Hibernate一些與一級緩存相關的操作(時間點):

資料放入緩存:

1. save()。當session對象調用save()方法儲存一個對象後,該對象會被放入到session的緩存中。

2. get()和load()。當session對象調用get()或load()方法從資料庫取出一個對象後,該對象也會被放入到session的緩存中。

3. 使用HQL和QBC等從資料庫中查詢資料。

例如:資料庫有一張表如下:

使用get()或load()證明緩存的存在:

public class Client
{
    public static void main(String[] args)
    {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = null;
        try
        {
            /*開啟一個事務*/
            tx = session.beginTransaction();
            /*從資料庫中擷取id="402881e534fa5a440134fa5a45340002"的Customer對象*/
            Customer customer1 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");
            System.out.println("customer.getUsername is"+customer1.getUsername());
            /*事務送出*/
            tx.commit();
            
            System.out.println("-------------------------------------");
            
            /*開啟一個新事務*/
            tx = session.beginTransaction();
            /*從資料庫中擷取id="402881e534fa5a440134fa5a45340002"的Customer對象*/
            Customer customer2 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");
            System.out.println("customer2.getUsername is"+customer2.getUsername());
            /*事務送出*/
            tx.commit();
            
            System.out.println("-------------------------------------");
            
            /*比較兩個get()方法擷取的對象是否是同一個對象*/
            System.out.println("customer1 == customer2 result is "+(customer1==customer2));
        }
        catch (Exception e)
        {
            if(tx!=null)
            {
                tx.rollback();
            }
        }
        finally
        {
            session.close();
        }
    }
}      

程式控制台輸出結果:

Hibernate: 
    select
        customer0_.id as id0_0_,
        customer0_.username as username0_0_,
        customer0_.balance as balance0_0_ 
    from
        customer customer0_ 
    where
        customer0_.id=?
customer.getUsername islisi
-------------------------------------
customer2.getUsername islisi
-------------------------------------
customer1 == customer2 result is true      

  輸出結果中隻包含了一條SELECT SQL語句,而且customer1 == customer2 result is true說明兩個取出來的對象是同一個對象。其原理是:第一次調用get()方法, Hibernate先檢索緩存中是否有該查找對象,發現沒有,Hibernate發送SELECT語句到資料庫中取出相應的對象,然後将該對象放入緩存中,以便下次使用,第二次調用get()方法,Hibernate先檢索緩存中是否有該查找對象,發現正好有該查找對象,就從緩存中取出來,不再去資料庫中檢索。

資料從緩存中清除:

1. evit()将指定的持久化對象從緩存中清除,釋放對象所占用的記憶體資源,指定對象從持久化狀态變為脫管狀态,進而成為遊離對象。

2. clear()将緩存中的所有持久化對象清除,釋放其占用的記憶體資源。

其他緩存操作:

1. contains()判斷指定的對象是否存在于緩存中。

2. flush()重新整理緩存區的内容,使之與資料庫資料保持同步。

Hibernate使用二級緩存

适合存放到第二級緩存中的資料:

1. 很少被修改的資料。

2. 不是很重要的資料,允許出現偶爾并發的資料。

3. 不會被并發通路的資料。

4. 參考資料,指的是供應用參考的常量資料,它的執行個體數目有限,它的執行個體會被許多其他類的執行個體引用,執行個體極少或者從來不會被修改。

不适合存放到第二級緩存的資料:

1. 經常被修改的資料。

2. 财務資料,絕對不允許出現并發。

3. 與其他應用共享的資料。

  Hibernate如何将資料庫中的資料放入到二級緩存中?注意,你可以把緩存看做是一個Map對象,它的Key用于存儲對象OID,Value用于存儲POJO。首先,當我們使用Hibernate從資料庫中查詢出資料,擷取檢索的資料後,Hibernate将檢索出來的對象的OID放入緩存中key中,然後将具體的POJO放入value中,等待下一次再次向資料查詢資料時,Hibernate根據你提供的OID先檢索一級緩存,若沒有且配置了二級緩存,則檢索二級緩存,如果還沒有則才向資料庫發送SQL語句,然後将查詢出來的對象放入緩存中。

為Hibernate配置二級緩存:

在主配置檔案中hibernate.cfg.xml

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

配置ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <!--
        緩存到硬碟的路徑
    -->
    <diskStore path="d:/ehcache"></diskStore>
    
    <!--
        預設設定
        maxElementsInMemory : 在內存中最大緩存的對象數量。
        eternal : 緩存的對象是否永遠不變。
        timeToIdleSeconds :可以操作對象的時間。
        timeToLiveSeconds :緩存中對象的生命周期,時間到後查詢資料會從資料庫中讀取。
        overflowToDisk :記憶體滿了,是否要緩存到硬碟。
    -->
    <defaultCache maxElementsInMemory="200" eternal="false" 
        timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache>
        
    <!--
        指定緩存的對象。
        下面出現的的屬性覆寫上面出現的,沒出現的繼承上面的。
    -->
    <cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false" 
        timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache>

</ehcache>      
<hibernate-mapping>
        <class name="com.suxiaolei.hibernate.pojos.Order" table="orders">
            <cache usage="read-only"/>
            
            <id name="id" type="string">
                <column name="id"></column>
                <generator class="uuid"></generator>
            </id>
            
            <property name="orderNumber" column="orderNumber" type="string"></property>
            <property name="cost" column="cost" type="integer"></property>
            
            <many-to-one name="customer" class="com.suxiaolei.hibernate.pojos.Customer" 
                         column="customer_id" cascade="save-update">
            </many-to-one>        
        </class>
    </hibernate-mapping>      
<hibernate-mapping>
        <class name="com.suxiaolei.hibernate.pojos.Customer" table="customer">
            <!-- 主鍵設定 -->
            <id name="id" type="string">
                <column name="id"></column>
                <generator class="uuid"></generator>
            </id>
            
            <!-- 屬性設定 -->
            <property name="username" column="username" type="string"></property>
            <property name="balance" column="balance" type="integer"></property>
            
            <set name="orders" inverse="true" cascade="all" lazy="false" fetch="join">
                <cache usage="read-only"/>
                <key column="customer_id" ></key>
                <one-to-many class="com.suxiaolei.hibernate.pojos.Order"/>
            </set>
            
        </class>
    </hibernate-mapping>      

繼續閱讀