Hibernate三種狀態,緩存,以及update更新問題
一. Hibernate中對象的三種狀態
1. 瞬時狀態(transient)
當我們通過Java的new關鍵字來生成一個實體對象時,這時這個實體對象就處於自由狀態,此時該對象只是通過JVM獲得了一塊內存空間,還並沒有通過Session對象的save()方法保存進數據庫,因此也就還沒有納入Hibernate的緩存管理中,也就是說該對象現在還自由的遊蕩於Hibernate緩存管理之外。所以我們可以看出自由對象最大的特點就是,在數據庫中不存在一條與它對應的記錄。
瞬時狀態特點:
I: 不和 Session 實例關聯;
II: 在數據庫中沒有和瞬時對象關聯的記錄
2. 持久狀態(persistent)
持久化對象就是已經被保存進數據庫的實體對象,並且這個實體對象現在還處於Hibernate的緩存管理之中。這是對該實體對象的任何修改,都會在清理緩存時同步到數據庫中。
持久狀態特點:
I: Session 實例關聯
II: 在數據庫中有和持久對象關聯的記錄
3. 遊離狀態(detached)
當一個持久化對象,脫離開Hibernate的緩存管理後,它就處於遊離狀態,遊離對象和自由對象的最大區別在於,遊離對象在數據庫中可能還存在一條與它對應的記錄,只是現在這個遊離對象脫離了Hibernate的緩存管理,而自由對象不會在數據庫中出現與它對應的數據記錄。
脫管對象特點:
I: 本質上和瞬時對象相同
II: 只是比愛瞬時對象多了一個數據庫記錄標識值 id.
三種狀態相互轉化的狀態圖如下:
二. Hibernate執行update操作會訪問數據庫幾次
首先你要知道Hibernate執行update的運行機理,Hibernate的update是怎麽樣自動進行update操作的呢?
(1). hibernate會執行一個select操作,到數據庫中查找,當前要update操作的對象的主鍵是否存在,類似於:
select id from table where id=xxx;
(2).執行更新操作
I: 如果查到了該ID,就說明該對象是一個持久化對象,如果該對象的某些屬性變化了,Hibernate就會自動的執行update操作,同步數據庫中的該對象。
II: 如果Hibernate沒有查找到該ID,就說明該對象是一個遊離的對象,Hibernate就會自行insert操作。
根據這些,就可以明白要update的對象的ID在數據庫中不存在,或者更改該對象的ID,這些都是執行insert而不是update。
三. Hibernate中一級緩存和二級緩存
1. 一級緩存二級緩存的概念解析
(1)一級緩存就是Session級別的緩存,一個Session做了一個查詢操作,它會把這個操作的結果放在一級緩存中,如果短時間內這個
session(一定要同一個session)又做了同一個操作,那麽hibernate直接從一級緩存中拿,而不會再去連數據庫,取數據;
(2)二級緩存就是SessionFactory級別的緩存,顧名思義,就是查詢的時候會把查詢結果緩存到二級緩存中,如果同一個sessionFactory
創建的某個session執行了相同的操作,hibernate就會從二級緩存中拿結果,而不會再去連接數據庫;
(3)Hibernate中提供了兩級Cache,第一級別的緩存是Session級別的緩存,它是屬於事務範圍的緩存。這一級別的緩存由hibernate管理
的,一般情況下無需進行幹預;第二級別的緩存是SessionFactory級別的緩存,它是屬於進程範圍或群集範圍的緩存。這一級別的緩
存可以進行配置和更改,並且可以動態加載和卸載。 Hibernate還為查詢結果提供了一個查詢緩存,它依賴於第二級緩存;
2. 一級緩存二級緩存的比較
(1)第一級緩存 第二級緩存 存放數據的形式相互關聯的持久化對象 對象的散裝數據 緩存的範圍事務範圍,每個事務都有單獨的第一級
緩存進程範圍或集群範圍,緩存被同一個進程或集群範圍內的所有事務共享並發訪問策略由於每個事務都擁有單獨的第一級緩存,不
會出現並發問題,無需提供並發訪問策略由於多個事務會同時訪問第二級緩存中相同數據,因此必須提供適當的並發訪問策略,來保
證特定的事務隔離級別數據過期策略沒有提供數據過期策略。
(2)處於一級緩存中的對象永遠不會過期,除非應用程序顯式清空緩存或者
清除特定的對象必須提供數據過期策略,如基於內存的緩存中的對象的最大數目,允許對象處於緩存中的最長時間,以及允許對象處
於緩存中的最長空閑時間物理存儲介質內存內存和硬盤。
(3)對象的散裝數據首先存放在基於內存的緩存中,當內存中對象的數目達到數
據過期策略中指定上限時,就會把其余的對象寫入基於硬盤的緩存中。
(4)緩存的軟件實現在Hibernate的Session的實現中包含了緩存的
實現由第三方提供,Hibernate僅提供了緩存適配器(CacheProvider)。用於把特定的緩存插件集成到Hibernate中。
(5)啟用緩存的方式
只要應用程序通過Session接口來執行保存、更新、刪除、加載和查詢數據庫數據的操作,Hibernate就會啟用第一級緩存,把數據庫
中的數據以對象的形式拷貝到緩存中,對於批量更新和批量刪除操作,如果不希望啟用第一級緩存,可以繞過Hibernate API,直接
通過JDBC API來執行指操作。
(6)用戶可以在單個類或類的單個集合的粒度上配置第二級緩存。如果類的實例被經常讀但很少被修改,就
可以考慮使用第二級緩存。
(7)只有為某個類或集合配置了第二級緩存,Hibernate在運行時才會把它的實例加入到第二級緩存中。
用戶管理緩存的方式第一級緩存的物理介質為內存,由於內存容量有限,必須通過恰當的檢索策略和檢索方式來限制加載對象的數目。
Session的 evit()方法可以顯式清空緩存中特定對象,但這種方法不值得推薦。第二級緩存的物理介質可以是內存和硬盤,因此第二
級緩存可以存放大量的數據,數據過期策略的maxElementsInMemory屬性值可以控制內存中的對象數目。
(8)管理第二級緩存主要包括兩個方面:選擇需要使用第二級緩存的持久類,設置合適的並發訪問策略:選擇緩存適配器,設置合適的數據過期策略。
3. 一級緩存的管理
(1)當應用程序調用Session的save()、update()、savaeOrUpdate()、get()或load(),以及調用查詢接口的 list()、iterate()或
filter()方法時,如果在Session緩存中還不存在相應的對象,Hibernate就會把該對象加入到第一級緩存中。當清理緩存時,
Hibernate會根據緩存中對象的狀態變化來同步更新數據庫。 Session為應用程序提供了兩個管理緩存的方法: evict(Object obj)
:從緩存中清除參數指定的持久化對象。 clear():清空緩存中所有持久化對象。
(2)save、update、saveOrupdate、load、list、iterate、lock會向一級緩存存放數據;
1 save 案例: 2 //添加一個學生 3 Student student=new Student(); 4 student.setName("小東"); 5 6 s.save(student);//放入一級緩存 7 8 //我馬上查詢 9 Student stu2=(Student) s.get(Student.class, student.getId()); //select 10 System.out.println("你剛剛加入的學生名字是"+stu2.getName());
(3)什麽操作會從一級緩存取數據:get、load、list
get / load 會首先從一級緩存中取,如沒有.再有不同的操作[get 會立即向數據庫發請求,而load 會返回一個代理對象,直到用戶真的去使用數據,才會向數據庫發請求;
//查詢45號學生
1 save 案例: 2 //添加一個學生 3 Student student=new Student(); 4 student.setName("小東"); 5 6 s.save(student);//放入一級緩存 7 8 //我馬上查詢 9 Student stu2=(Student) s.get(Student.class, student.getId()); //select 10 System.out.println("你剛剛加入的學生名字是"+stu2.getName());
從上面的案例,我們看出 query.list() query.uniueResut() 不會從一級緩取數據! 但是query.list 或者query.uniqueRestu() 會向一級緩存放數據的.
註意:
① 一級緩存不需要配置,就可以使用,它本身沒有保護機制,所以我們程序員要考慮這個問題,我們可以同 evict 或者 clear來清除session緩存中對象. evict 是清除一個對象,clear是清除所有的sesion緩存對象
② session級緩存中對象的生命周期, 當session關閉後,就自動銷毀.
③ 我們自己用HashMap來模擬一個Session緩存,加深對緩存的深入.
4.Hibernate二級緩存的管理
I. Hibernate二級緩存策略的一般過程如下:
1) 條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有字段)這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。
2) 把獲得的所有數據對象根據ID放入到第二級緩存中。
3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那麽從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。
4) 刪除、更新、增加數據的時候,同時更新緩存。
Hibernate二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無作用。為此,Hibernate提供了針對條件查詢的Query Cache。
5) 二級緩存的對象可能放在內存,也可能放在磁盤.
II. 什麽樣的數據適合存放到第二級緩存中?
1) 很少被修改的數據
2) 不是很重要的數據,允許出現偶爾並發的數據
3) 不會被並發訪問的數據
4) 參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其他類的實例引用,實例極少或者從來不會被修改。
III. 不適合存放到第二級緩存的數據?
1) 經常被修改的數據
2) 財務數據,絕對不允許出現並發
3) 與其他應用共享的數據。
IV. 常用的緩存插件 Hibernater二級緩存是一個插件,下面是幾種常用的緩存插件:
◆Ehcache:可作為進程範圍的緩存,存放數據的物理介質可以是內存或硬盤,對Hibernate的查詢緩存提供了支持。
◆OSCache:可作為進程範圍的緩存,存放數據的物理介質可以是內存或硬盤,提供了豐富的緩存數據過期策略,對Hibernate的查詢
緩存提供了支持。
◆SwarmCache:可作為群集範圍內的緩存,但不支持Hibernate的查詢緩存。
◆JBossCache:可作為群集範圍內的緩存,支持事務型並發訪問策略,對Hibernate的查詢緩存提供了支持。
V. 配置Hibernate二級緩存的主要步驟:
1) 選擇需要使用二級緩存的持久化類,設置它的命名緩存的並發訪問策略。這是最值得認真考慮的步驟。
2) 選擇合適的緩存插件,然後編輯該插件的配置文件。
1 <property name="hbm2ddl.auto">update</property> 2 <!-- 啟動二級緩存 --> 3 <property name="cache.use_second_level_cache">true</property> 4 <!-- 指定使用哪種二級緩存 --> 5 <property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property> 6 <mapping resource="com/hsp/domain/Department.hbm.xml" /> 7 <mapping resource="com/hsp/domain/Student.hbm.xml" /> 8 <!-- 指定哪個domain啟用二級緩存 9 特別說明二級緩存策略: 10 1. read-only 11 2. read-write 12 3. nonstrict-read-write 13 4. transcational 14 --> 15 <class-cache usage="read-write"/>
3)可以把oscache.properties文件放在 src目錄下,這樣你可以指定放入二級緩存的對象capacity 大小. 默認1000
VI.使用二級緩存:
1 // TODO Auto-generated method stub 2 //通過獲取一個sesion,讓hibernate框架運行(config->加載hibernate.cfg.xml) 3 Session s=null; 4 Transaction tx=null; 5 6 try { 7 //我們使用基礎模板來講解. 8 s=HibernateUtil.openSession(); 9 tx=s.beginTransaction(); 10 11 //查詢45號學生 12 13 Student stu1=(Student) s.get(Student.class, 45);//45->一級緩存 14 System.out.println(stu1.getName()); 15 16 tx.commit(); 17 18 } catch (Exception e) { 19 e.printStackTrace(); 20 if(tx!=null){ 21 tx.rollback(); 22 } 23 }finally{ 24 25 if(s!=null && s.isOpen()){ 26 s.close(); 27 } 28 } 29 30 System.out.println("*********************************"); 31 try { 32 //我們使用基礎模板來講解. 33 s=HibernateUtil.openSession(); 34 tx=s.beginTransaction(); 35 36 //查詢45號學生 37 38 Student stu1=(Student) s.get(Student.class, 45); 39 System.out.println(stu1.getName()); 40 41 Student stu3=(Student) s.get(Student.class, 46); 42 System.out.println(stu3.getName()); 43 tx.commit(); 44 45 } catch (Exception e) { 46 e.printStackTrace(); 47 if(tx!=null){ 48 tx.rollback(); 49 } 50 }finally{ 51 52 if(s!=null && s.isOpen()){ 53 s.close(); 54 } 55 } 56 57 //完成一個統計,統計的信息在Sessfactory 58 //SessionFactory對象. 59 Statistics statistics= HibernateUtil.getSessionFactory().getStatistics(); 60 System.out.println(statistics); 61 System.out.println("放入"+statistics.getSecondLevelCachePutCount()); 62 System.out.println("命中"+statistics.getSecondLevelCacheHitCount()); 63 System.out.println("錯過"+statistics.getSecondLevelCacheMissCount());
在配置了二級緩存後,請大家要註意可以通過 Statistics,查看你的配置命中率高不高
Hibernate三種狀態,緩存,以及update更新問題