Hibernate一級緩存 & 二級緩存

1、一級緩存Session的操做與維護java

 

1.Hibernate對象的三種狀態transientpersistentdetachedweb

 

1) transient:瞬時狀態           sql

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

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

 

2) persistent:持久化狀態併發

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

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

能夠將一個瞬時狀態轉變爲持久化對象sqlserver

 

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 = ?(單個對象實施記載)

  

2、二級緩存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參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其餘類的實例引用,實例極少或者歷來不會被修改。

相關文章
相關標籤/搜索