Hibernate一級緩存 & 二級緩存

openkk 14年前發布 | 56K 次閱讀 Hibernate 持久層框架

一、一級緩存Session的操作與維護

 

1.Hibernate對象的三種狀態transientpersistentdetached

 

1) transient:瞬時狀態           

利用new關鍵字創建的對象,沒有與Hibernate實施交互的,也無法保證與數據庫中某條記錄對應的對象被稱為瞬時狀態,也就是生命周期非常短的意思,因為沒有任何組件管理的對象非常容易被Java虛擬機回收。

:Customer cus = new Customer();//瞬時狀態對象

 

2) persistent:持久化狀態

將瞬時狀態的對象保存到Hibernate緩存中,受Hibernate管理的對象被稱為持久化狀態Hibernate在調用flushclose ,clear方法的時候會清理緩存保證持久化對象與底層數據庫的同步,所有的持久化對象,Hibernate均會為它設置一個唯一性標識符保證其在緩存中的唯一性,這個標識符可能是hashCode,帶主鍵查詢的sql語句或者其他保證唯一的字符。

save(new object),update(new object),saveorupdate(new object),persisnt(new object)

可以將一個瞬時狀態轉變為持久化對象

 

save(new object), persistent (new object):利用select sql where id 加載一個對象

如果加載成功那么直接顯示異常(插入不允許帶相同標識符的組件在緩存中存在)

 

update(new object):不執行SQL語句,直接將對象加載入內存做更新準備

如果緩存中已經擁有一個同標識符的組件,那么顯示異常,因為update只能做更新處理

無法處理兩個完全一致的對象(merge(new object)可以克服這個問題)

 

saveorupdate(new object):利用select sql where id 加載一個對象

如果加載成功那么直接更新,否則插入

 

注意:業務層/表示層/應用層對于持久化狀態對象的更改都會引起Hibernate的同步處理(反映到數據庫中)

 

3) detached:游離托管狀態

緩存中已經失去該對象或者該對象的標識符,雖然對象仍然存在,對象也和數據表中的記錄有對應但是由于失去了Hibernate的控制,因此該對象被稱為游離托管狀態。

 

注意:業務層/表示層/應用層對于托管狀態對象的更改不會會引起Hibernate的同步處理

 

游離托管狀態的對象是指:已經經過Hibernate管理后,

因為Session.delete(),Session.close(),Session.clear(),Session.evict()

方法的執行而失去標識符的,所以托管狀態和瞬時狀態的對的區別是是否受過Hibernate的管理,數據表中是否有對應的記錄,托管對象只有通過lock(),update(),saveorupdate()被重新加入緩存變成持久化對象才能實施數據同步

 

2、特殊方法:persisnt(),merge()

persisntsave方法是差不多的,唯一的區別是針對sqlserveridentity字段的處理save為了保證持久化標識符,所以會在save的過程中就直接執行insert into....select identity();以獲取最新的主鍵信息。

persisnt執行的時機是Transaction.commit()Session.clear(),Session.flush()的時候

 

merge()update()類似,但是區別是對于瞬時狀態的理解。

假設現在有一個瞬時狀態的new Customer(1),同時Session利用get(),load()方法

產生一個持久化狀態的對象Session.get(Customer.class,1),這個時候使用update方法會拋出異常,而merge會將瞬時狀態Customer中的屬性復制到持久化狀態的Customer

中。

 

3、查詢對于一級緩存的使用

1Session.getSession.load方法

Session.get:迫切加載

第一查詢:查詢一級緩存Session緩存,如果沒有那么查詢二級緩存SessionFactory緩存(如果沒有配置,此步省略),如果找不到那么執行Select…..From…..Where id = ?,如果數據被查到那么將查到的數據封裝到對象中,對象被分別保存在一級緩存Session緩存中,和二級緩存SessionFactory緩存(如果沒有配置,此步省略)。

第二查詢:查詢一級緩存Session緩存,,如果找不到那么執行Select…..From…..Where id = ?,如果數據被查到那么將查到的數據封裝到對象中,對象被保存在一級緩存Session緩存中。

Session.load:延遲加載

Session.load認為加載始終是成功的,所以它始終不會與數據庫交互,因為load認為查詢一定會成功,因此只有當需要訪問被加載實體屬性的時候,Session.load才會按照Session.get的查詢軌跡實施搜尋,不同的是Session.load始終會查詢一,二級緩存再執行SQL語句

如果SQL語句無法返回對象,那么Session.load直接拋出異常。

 

2Query.listQuery.iterator方法

Query.list查詢過程中不會讀取任何緩存尤其是一級緩存,而是直接執行SQL語句,SQL語句有QueryHQL轉換而得,執行結果會被保存在一級緩存中。

我們可以通過ehcache緩存組件為Hibernate配置查詢緩存(query cache[hibernate 3.x以上版本]),這Query.list會每次查詢的過程中先訪問二級緩存中的查詢緩存,如果沒有再執行SQL語句,查詢返回的結果會分別保存在一,二級緩存中。

 

Query.iterator:與Query.list的不同在于,它會每次訪問均查詢一級緩存,但是

Query.iterator記載數據的方式不是完整的SQL語句,而是N+1SQL語句

例如:Query.list 對于一張5條記錄的表的檢索方式是Select ….. From Customer

Query.iterator的檢索方式是:

執行5Select ….. From Customer where id = ?(單個對象實施記載)

  

二、二級緩存SessionFactory 的配置與測試

1. 利用ehcache配置Second level cache 和 Query cache

src目錄下建立ehcache.xml文件內容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<ehcache>
<!—
    設置對象鈍化目錄,二級緩存中長時間不使用或者超時的對象
    會被保存在當前目錄\java\io\tmpdir目錄中,這樣可以節省空間 
-->
<diskStore path="java.io.tmpdir"/>

</ehcache>

<!-- 默認二級緩存工作模式
         maxElementsInMemory:緩存中最大對象數
         eternal:緩存中的對象是否永久保留
         timeToIdleSeconds:多少毫秒后可以考慮一個對象的放入鈍化目錄中
         timeToLiveSeconds:多少毫秒后可以考慮一個對象從激活狀態-閑置狀態
         overflowToDisk:是否允許將閑置對象鈍化入硬盤
         diskPersistent:鈍化后該對象是否允許永久無法反鈍化
         diskExpiryThreadIntervalSeconds:鈍化線程間隔處理時間(毫秒)
         memoryStoreEvictionPolicy:鈍化對象選擇模型(LRU:使用最少的先鈍化技術)
    -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"

            timeToLiveSeconds="120"
            overflowToDisk="true"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"

            />

<!-- 默認二級查詢緩存工作模式-->
   <cache name="org.hibernate.cache.StandardQueryCache"
            maxElementsInMemory="10000" 
            eternal="false" 
            timeToIdleSeconds="1800" 
            timeToLiveSeconds="0" 
            overflowToDisk="true"/>

<!-- 為時間郵差準備的二級查詢緩存工作模式
         主要有同步數據方法調用,例如lock(LockMode.READ)
    -->         
   <cache name="org.hibernate.cache.UpdateTimestampsCache"
            maxElementsInMemory="5000"
            eternal="true"
            timeToIdleSeconds="1800"

            timeToLiveSeconds="0"             
            overflowToDisk="true"/>

2. 配置h 注意:緩存監控方法如果要能夠執行,需要在hibernate.cfg.xml中設置以下配置

ibernate.cfg..xml

<!-- 使用ehcache第三方緩存技術 -->
    <property name="cache.provider_class">
        net.sf.ehcache.hibernate.EhCacheProvider
    </property>
    <!-- 啟用運行查詢緩存 -->
    <property name="cache.use_query_cache">true</property>

    <!-- 啟用二級緩存(SessionFactory緩存) -->
    <property 
    name="cache.use_second_level_cache">true</property>

3. 為需要二級緩存管理的對象設置標識列(*.hbm.xml)

<hibernate-mapping>
 <class name="cn.newtouch.myhibernate.po.Customer" schema="HIBERNATE" table="CUSTOMER">

  <!— 表示Customer需要受到緩存管理 -->
    <cache usage="read-write"/>
  <id name="id" type="java.lang.Long">
   <column name="ID" precision="8" scale="0"/>

   <generator class="assigned"/>
  </id>

4. 測試實體查詢對于二級緩存的執行模式:

public void testSecondLevelCacheForEntityQuery() {
        Configuration cfg = new Configuration().configure();
        SessionFactory factory = cfg.buildSessionFactory();
        Session cnn = factory.openSession();
        SessionStatistics ss = cnn.getStatistics();
        Statistics s = factory.getStatistics();
        cnn.get(Customer.class, 1l);
        System.out.println("FIRST GET:" +ss.getEntityCount());
        System.out.println("FIRST GET PUT:" +
                                  s.getSecondLevelCachePutCount());
        System.out.println("FIRST GET MISS:" + 
    s.getSecondLevelCacheMissCount());
        System.out.println("FIRST GET HIT:" + 
    s.getSecondLevelCacheHitCount());    
        cnn.get(Customer.class, 1l);

        System.out.println("SECOND GET:" +ss.getEntityCount());
        System.out.println("SECOND GET PUT:" + 
    s.getSecondLevelCachePutCount());
        System.out.println("SECOND GET MISS:" + 
    s.getSecondLevelCacheMissCount());
        System.out.println("HIT:" + 
    s.getSecondLevelCacheHitCount());
        cnn.clear();
        System.out.println("BEFORE CLEAR:" + ss.getEntityCount());
        System.out.println("BEFORE CLEAR PUT:" + 
    s.getSecondLevelCachePutCount());
        System.out.println("BEFORE CLEAR MISS:" + 
    s.getSecondLevelCacheMissCount());
        System.out.println("BEFORE CLEAR HIT:" + 
    s.getSecondLevelCacheHitCount());

    Session anotherSession = factory.openSession();
        anotherSession.get(Customer.class, 1l);
        System.out.println("ANOTHER SESSION:" + 
    ss.getEntityCount());
        System.out.println("ANOTHER SESSION PUT:" + 
    s.getSecondLevelCachePutCount());
        System.out.println("ANOTHER SESSION MISS:" + 
    s.getSecondLevelCacheMissCount());
        System.out.println("ANOTHER SESSION HIT:" + 
    s.getSecondLevelCacheHitCount());
        anotherSession.clear();
    }

測試二級緩存的結果是

FIRST GET:1
FIRST GET PUT:1
FIRST GET MISS:1
FIRST GET HIT:0
=======================================
SECOND GET:1
SECOND GET PUT:1
SECOND GET MISS:1
SECOND GET HIT:0
=======================================
BEFORE CLEAR:0
BEFORE CLEAR PUT:1
BEFORE CLEAR MISS:1
BEFORE CLEAR HIT:0
=======================================
ANOTHER SESSION:0
ANOTHER SESSION PUT:1
ANOTHER SESSION MISS:1
ANOTHER SESSION HIT:1

5. 測試HQL查詢對于二級緩存的執行模式

public void testSecondLevelCacheForQueries() {

        Configuration cfg = new Configuration().configure();
        SessionFactory factory = cfg.buildSessionFactory();
        Session cnn = factory.openSession();

    SessionStatistics ss = cnn.getStatistics();
        Statistics s = factory.getStatistics();

    Query query = 
            cnn.createQuery("From Customer A");
        query.setCacheable(true);
    query.setCacheRegion(
    "cn.newtouch.myhibernate.po.Customer");
        query.list();
        System.out.println("FIRST Query:" +ss.getEntityCount());
System.out.println("Level's PUT:" + 
    s.getSecondLevelCachePutCount());
System.out.println("Level's MISS:" + 
    s.getSecondLevelCacheMissCount());
System.out.println("Level's HIT:" +    
    s.getSecondLevelCacheHitCount());
System.out.println("Queries's PUT:" + 
    s.getQueryCachePutCount());
System.out.println("Queries's MISS:" + 
    s.getQueryCacheMissCount());
System.out.println("Queries's HIT:" + 
    s.getQueryCacheHitCount());
System.out.println("=======================================");

        query = cnn.createQuery("From Customer A");
        query.setCacheable(true);
    query.setCacheRegion(
    "cn.newtouch.myhibernate.po.Customer");
        query.list();

System.out.println("SECOND Query:" +ss.getEntityCount());
System.out.println("Level's PUT:" + 
    s.getSecondLevelCachePutCount());
System.out.println("Level's MISS:" + 
    s.getSecondLevelCacheMissCount());
System.out.println("Level's HIT:" +    
    s.getSecondLevelCacheHitCount());
System.out.println("Queries's PUT:" + 
    s.getQueryCachePutCount());
System.out.println("Queries's MISS:" + 
    s.getQueryCacheMissCount());
System.out.println("Queries's HIT:" + 
    s.getQueryCacheHitCount());
System.out.println("=======================================");

        cnn.clear();
        System.out.println("BEFORE CLEAR:" +ss.getEntityCount());
System.out.println("Level's PUT:" + 
    s.getSecondLevelCachePutCount());
System.out.println("Level's MISS:" + 
    s.getSecondLevelCacheMissCount());
System.out.println("Level's HIT:" +    
    s.getSecondLevelCacheHitCount());
System.out.println("Queries's PUT:" + 
    s.getQueryCachePutCount());
System.out.println("Queries's MISS:" + 
    s.getQueryCacheMissCount());
System.out.println("Queries's HIT:" + 
    s.getQueryCacheHitCount());
System.out.println("=======================================");

        Session anotherSession = factory.openSession();

        query = 
            anotherSession.createQuery("From Customer A");
        query.setCacheable(true);
    query.setCacheRegion(
    "cn.newtouch.myhibernate.po.Customer");
        query.list();

System.out.println("ANOTHER SESSION:" +ss.getEntityCount());
System.out.println("Level's PUT:" + 
    s.getSecondLevelCachePutCount());
System.out.println("Level's MISS:" + 
    s.getSecondLevelCacheMissCount());
System.out.println("Level's HIT:" +    
    s.getSecondLevelCacheHitCount());
System.out.println("Queries's PUT:" + 
    s.getQueryCachePutCount());
System.out.println("Queries's MISS:" + 
    s.getQueryCacheMissCount());
System.out.println("Queries's HIT:" + 
    s.getQueryCacheHitCount());
System.out.println("=======================================");

        anotherSession.clear();
}

測試二級緩存的結果是:

FIRST Query:2
Level's PUT:2
Level's MISS:0
Level's HIT:0
Queries's PUT:1
Queries's MISS:1
Queries's HIT:0
=======================================
SECOND Query:2
Level's PUT:2
Level's MISS:0
Level's HIT:0
Queries's PUT:1
Queries's MISS:1
Queries's HIT:1
=======================================
BEFORE CLEAR:0
Level's PUT:2
Level's MISS:0
Level's HIT:0
Queries's PUT:1
Queries's MISS:1
Queries's HIT:1
=======================================
ANOTHER SESSION:0
Level's PUT:2
Level's MISS:0
Level's HIT:2
Queries's PUT:1
Queries's MISS:1
Queries's HIT:2


注意:緩存監控方法如果要能夠執行,需要在hibernate.cfg.xml中設置以下配置

<property name="generate_statistics">true</property>

以下情況適合使用二級緩存:

1很少被修改的數據

2不是很重要的數據,允許出現偶爾并發的數據 

3不會被并發訪問的數據

4參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其他類的實例引用,實例極少或者從來不會被修改。


 本文由用戶 openkk 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!