1. 程式人生 > >hibernate session快取機制

hibernate session快取機制


              Hibernate
向我們提供的主要的操縱資料庫的介面,Session就是其中的一個,它提供了基本的增,,,查方法.而且具有一個快取機制,能夠按照某個時間點,按照快取中的持久化物件屬性的變化來更新資料庫,著就是Session的快取清理過程.Hibernate中物件分為三個狀態,臨時,持久化,遊離.如果我們希望JAVA裡的一個物件一直存在,就必須有一個變數一直引用著這個物件.當這個變數沒了.物件也就被JVM回收了.這篇部落格我們就帶大家一起來看一下session的快取機制,也就是hibernate的一級快取,還有hibernate三種物件狀態的詳細情況。

             當Session的save()方法持久化一個Customer物件時,Customer物件被加入到Session的快取中,以後即使應用程式中的引用變數不再引用Customer物件,只要Session的快取還沒有被清空,Customer物件仍然處於生命週期中。 當Session的load()方法試圖從資料庫中載入一個Customer物件時,Session先判斷快取中是否已經存在這個Customer物件,如果存在,就不需要再到資料庫中檢索。 這樣就大大提高了hibernate查詢的時間效率,只有當事務提交,session關閉之後,session快取才會失效      

下面我們來通過一段程式碼來理解一下session快取:        

tx = session.beginTransaction();   
Customer c1=new Customer(“zhangsan",new HashSet());   
//Customer物件被持久化,並且加入到Session的快取中   
session.save(c1);   
Long id=c1.getId();   
//c1變數不再引用Customer物件   
c1=null;   
//從Session快取中讀取Customer物件,使c2變數引用Customer物件   
Customer c2=(Customer)session.load(Customer.class,id);   
tx.commit();   
//關閉Session,清空快取   
session.close();   
//訪問Customer物件   
System.out.println(c2.getName());   
// c2變數不再引用Customer物件,此時Customer物件結束生命週期。   
c2=null;   

當session呼叫save儲存一個物件時,這個物件就被載入到session快取當中,其實呼叫save方法這裡有個細節,很多人都忽略了這個細節,就是save方法有一個返回值,返回一個seriaseble介面型別的資料,我們知道像基本資料型別的包裝型別都實現了這個介面,其實這個返回值我們可以理解為儲存物件的id,我們在很多時候都能用到這個返回值,這是一個應該注意的地方。當物件被save到快取中時,我們就可以呼叫物件的getid方法來獲得他的id了。在上面的示例中我們可以看到,雖然c1被複位null了,但是此時在session快取裡面還是有一個變數指向著該物件,所以該物件才不被垃圾回收器回收,當我們在此利用該物件的id去用load查詢時,其實還是去到session快取去找並且返回該物件,當session關閉後。快取清空。

下面我們在來看一個例子,來看一下get和load的另一個不同點:

tx = session.beginTransaction();   
Customer c1=(Customer)session.load(Customer.class,new Long(1));   
Customer c2=(Customer)session.load(Customer.class,new Long(1));   
System.out.println(c1==c2); // true or false ??   
tx.commit();   
session.close();   

 很明顯,這個示例最後打印出來的是true,因為他們獲得的是同一個例項,我們具體來分析一下,我們在執行這段程式碼時,細心的童鞋應該會發現,利用load去查詢物件時,沒有生成sql語句,這是為什麼呢?既然查詢到結果了,為什麼沒有生成出來sql語句呢。這就是我們要說的load和get方法的第二個不同的地方了,load方法在查詢時,其實是獲得的該物件的一個代理的物件,當我們用到查詢到的物件時,他才會去資料庫進行查詢,如上,如果我們呼叫c1.getName方法,這時就會打印出sql語句來,這時候他才真正的去資料庫查詢,而get方法,他在執行get的方法的時候就會去資料庫查詢,產生sql語句

Session快取的作用

(1)減少訪問資料庫的頻率。應用程式從記憶體中讀取持久化物件的速度顯然比到資料庫中查詢資料的速度快多了,因此Session的快取可以提高資料訪問的效能。 

(2)保證快取中的物件與資料庫中的相關記錄保持同步。當快取中持久化物件的狀態發生了變化,Session並不會立即執行相關的SQL語句,這使得Session能夠把幾條相關的SQL語句合併為一條SQL語句,以便減少訪問資料庫的次數,從而提高應用程式的效能。 

Session的清理快取

清理快取是指按照快取中物件的狀態的變化來同步更新資料庫,下面我們還是具體來看一段程式碼:以下程式程式碼對Customer的name屬性修改了兩次: 

tx = session.beginTransaction();   
Customer customer=(Customer)session.load(Customer.class,   
new Long(1));   
customer.setName("Jack");   
customer.setName("Mike");   
tx.commit();   

當Session清理快取時,只需執行一條update語句: 

update CUSTOMERS set NAME= 'Mike'…… where ID=1; 

其實第一次呼叫setName是無意義的,完全可以省略掉。

Session快取在什麼時候才清理呢?我們來看一下:

Session會在下面的時間點清理快取: 

1.當應用程式呼叫org.hibernate.Transaction的commit()方法的時候,commit()方法先清理快取,然後再向資料庫提交事務。 

2.當應用程式顯式呼叫Session的flush()方法的時候,其實這個方法我們幾乎很少用到,因為我們一般都是在完成一個事務才去清理快取,提交資料更改,這樣我們直接提交事務就可以。

Hibernate中java物件的三種狀態:

1、臨時狀態(transient):剛剛用new語句建立,還沒有被持久化,不處於Session的快取中。處於臨時狀態的Java物件被稱為臨時物件。 

2、持久化狀態(persistent):已經被持久化,加入到Session的快取中。處於持久化狀態的Java物件被稱為持久化物件。 

3、遊離狀態(detached):已經被持久化,但不再處於Session的快取中。處於遊離狀態的Java物件被稱為遊離物件。 

持久化狀態和臨時狀態的不同點在於:

1、物件持久化狀態時,他已經和資料庫打交道了,在資料庫裡面存在著該物件的一條記錄。

2、持久化狀態的物件存在於session的快取當中。

3、持久化狀態的物件有自己的OID。

遊離狀態的物件與持久化狀態的物件不同體現在遊離狀態的物件已經不處於session的快取當中,並且在資料庫裡面已經不存在該物件的記錄,但是他依然有自己的OID。

物件的狀態轉換

                                                        

       我們一起來分析一下這個狀態轉換圖,首先一個物件被new出來之後,他是出於臨時狀態的,然後呼叫save或者saveOrUpdate方法把物件轉換為持久化狀態,這裡的saveOrUpdate方法其實是一個偷懶的方法,我們以前用的所有的save方法的地方都可以修改為該方法,這個方法是在儲存資料之前先檢視一下這個物件是什麼狀態,如果是臨時狀態就儲存,如果是遊離狀態就進行更新。持久化狀態轉換成遊離狀態可以是在session關閉或者被清理快取時,在或者就是呼叫evict方法,這個方法就是強行把物件從session快取中清除。遊離狀態裝換為持久化狀態可以呼叫update方法,其實update方法主要的功能就是把物件從遊離狀態裝換成持久化狀態的,因為一般的更新其實不用這個方法也可以。

下面我們舉一個具體例項的看一下狀態轉換過程:

                                      

     這個圖需要大家仔細的理解一下,他很好的體現了物件生命週期的程序和物件狀態的轉換。

下面我們在用一個示例來看一下session的update方法是怎麼把一個遊離狀態的物件裝換成持久化的:

Customer customer=new Customer();   
customer.setName("Tom");   
Session session1=sessionFactory.openSession();   
Transaction tx1 = session1.beginTransaction();   
session1.save(customer);   
tx1.commit();   
session1.close(); //此時Customer物件變為遊離物件   
Session session2=sessionFactory.openSession();   
Transaction tx2 = session2.beginTransaction();   
customer.setName(“zhangsan") //在和session2關聯之前修改Customer物件的屬性   
session2.update(customer);   
customer.setName(“lisi"); //在和session2關聯之後修改Customer物件的屬性   
tx2.commit();   
session2.close();   

 當session1儲存完物件,然後事務關閉時,物件就變為遊離狀態了,此時我們在開啟一個session,利用update方法,在把物件和session關聯起來,然後修改他的屬性,提交事務之後,遊離狀態的物件一樣可以修改儲存到資料庫中,這裡雖然修改了兩次物件的屬性,但只會傳送一條sql語句,因為update在修改物件資料時,只有在事務提交時,他才會傳送sql語句進行提交。所以只有最後一條修改資訊管用。

總結一下Session的update()方法完成以下操作: 

(1)把Customer物件重新加入到Session快取中,使它變為持久化物件。 

(2)計劃執行一個update語句。值得注意的是Session只有在清理快取的時候才會執行update語句,並且在執行時才會把Customer物件當前的屬性值組裝到update語句中。因此,即使程式中多次修改了Customer物件的屬性,在清理快取時只會執行一次update語句。 

Web應用程式客戶層和業務邏輯層之間傳遞臨時物件和有利物件的過程:

                                                              

Session的二級快取 

Hibernate提供了兩級快取,第一級快取是Session的快取。由於Session物件的生命週期通常對應一個數據庫事務或者一個應用事務,因此它的快取是事務範圍的快取。第一級快取是必須的,不允許而且事實上也無法被卸除。在第一級快取中,持久化類的每個例項都具有惟一的OID。 第二級快取是一個可插拔的快取外掛,它由SessionFactory負責管理。由於SessionFactory物件的生命週期和應用程式的整個程序對應,因此第二級快取是程序範圍的快取。這個快取中存放的是物件的散裝資料。第二級快取是可選的,可以在每個類或每個集合的粒度上配置第二級快取。 

Hibernate二級快取結構