1、一級緩存Session的操做與維護java
1.Hibernate對象的三種狀態: transient, persistent, detachedweb
1) transient:瞬時狀態 sql
利用new關鍵字建立的對象,沒有與Hibernate實施交互的,也沒法保證與數據庫中某條記錄對應的對象被稱爲瞬時狀態,也就是生命週期很是短的意思,由於沒有任何組件管理的對象很是容易被Java虛擬機回收。數據庫
例:Customer cus = new Customer();//瞬時狀態對象緩存
2) persistent:持久化狀態併發
將瞬時狀態的對象保存到Hibernate緩存中,受Hibernate管理的對象被稱爲持久化狀態Hibernate在調用flush, close ,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()
persisnt和save方法是差很少的,惟一的區別是針對sqlserver的identity字段的處理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、查詢對於一級緩存的使用
1)Session.get, Session.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直接拋出異常。
2)Query.list, Query.iterator方法
Query.list:查詢過程當中不會讀取任何緩存尤爲是一級緩存,而是直接執行SQL語句,SQL語句有Query的HQL轉換而得,執行結果會被保存在一級緩存中。
咱們能夠經過ehcache緩存組件爲Hibernate配置查詢緩存(query cache[hibernate 3.x以上版本]),這Query.list會每次查詢的過程當中先訪問二級緩存中的查詢緩存,若是沒有再執行SQL語句,查詢返回的結果會分別保存在一,二級緩存中。
Query.iterator:與Query.list的不一樣在於,它會每次訪問均查詢一級緩存,可是
Query.iterator記載數據的方式不是完整的SQL語句,而是N+1條SQL語句
例如:Query.list 對於一張5條記錄的表的檢索方式是Select ….. From Customer
而Query.iterator的檢索方式是:
執行5句Select ….. 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、參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其餘類的實例引用,實例極少或者歷來不會被修改。