在java中,我們使用<code>user user = new user();</code>來建立一個java對象時,jvm會為其配置設定一塊記憶體空間,此時,這個對象被變量“user”引用,那麼它就會一直存在于記憶體中,而如果我們我們的“引用者user”更新了,<code>user user = new vipuser()</code>。那麼原來<code>new user()</code>不再被任何變量引用,它就會結束自己的生命周期,然後會被jvm的智能垃圾回收期回收處理,以免再占用記憶體。
從以上分析,我們知道了java對象存活的條件就是:被(至少一個)變量引用
同樣的,假設在我們使用hibernate通路資料庫擷取了一個小a對象,這個小a一樣有它的存活條件,但與一般java對象不同,即使我們沒有建立任何變量來引用小a,我們的小a還是能夠活得好好的,這是因為小a被hibernate的session緩存下來了。
在session接口的實作類中,我們定義了一系列的java集合來存放從資料庫中擷取的資料,隻要我們的session執行個體沒有結束聲明周期,那麼存放其中的對象就不會結束其生命周期。
比如我們來看下面的例子
運作程式,觀察我們的列印資訊:
hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from t_user2 user0_ where user0_.id=? user1_1擷取完畢,耗時:26毫秒,準備開始擷取user1_2 user1_2擷取完畢,耗時:0毫秒,準備開始擷取user2 true false 在我們擷取user1_2時,并沒有查詢資料庫而且擷取時間幾乎為0,說明是直接從緩存中讀取的,而在比較對象屬性中,user1_1和user1_2相等,說明它們的引用位址也相同,而且必定與session緩存中的引用位址一緻 從上面我們還能看到,session辨別緩存的不同對象,是通過對象類型和對象辨別符id共同判别的,一旦兩者一緻,session即判别為同一對象,同時,我們也可歸納出利用session查詢資料庫的過程:比如我們要查詢id為1的user,則查詢過程如下時序圖所示:
created with raphaël 2.1.0sessionsessionsession緩存session緩存資料庫資料庫:1. 查找緩存是否有對象類型為user且id為12. 找到并傳回2. 沒找到則查詢資料庫3. 傳回資料結果并緩存4. 傳回資料到引用變量
在session清理緩存(flush)時,會進行髒檢查,如果發現緩存中的最新資料與資料庫記錄不一緻,會将最新資料更新到資料庫中。
那麼,session是如何進行髒檢查的呢?難道每次清理前,針對所有的緩存資料通路資料庫來進行匹對?這樣效率太低了。實際上,在上述時序圖的第3步到第4步之間,session會将獲得的資料結果先copy一份(這份copy還未經任何處理,肯定是和資料庫記錄一緻的),再傳回給引用變量。這樣我們将所有最新的資料與最初copy的校對一下,一旦出現差異,就将最新資料更新到資料庫。
在我們每次針對引用變量修改對象屬性後,對應的session緩存中的資料也會被修改,這是顯然的,因為它們的所指向的記憶體位址是一緻的。但修改後,hibernate并不會馬上執行相應的資料庫操作,隻有在特定條件下,如session被清理或特定的方法被調用才會通路資料庫。這裡談談session被清理的三個時間點:
1. 在完成事務送出之前,session會被清理一次。這樣的好處是一方面可以減少在事務作用過程中,大量執行的資料庫記錄修改操作。另一方面還可以盡可能縮短目前事務對相關資源的鎖定時間
2. 在執行一些複雜的查詢操作時,需要清理緩存,更新資料庫,確定查詢得到的資料是最新的。
3. 顯示地調用session.flush()方法
如果我們不希望在上述的某些時刻清理,我們可以通過session的setflushmode()方法來定制,它提供了3種模式共我們選擇:
模式
複雜查詢方法被執行
事務送出時
顯式調用flush()
使用場景
flushmode.auto(預設模式)
清理
正常應用場景
flushmode.commit
不清理
需要避免過多查詢操作清理緩存以提高性能的場景
flushmode.never
需要長時間運作的複雜事務場景
tips:從上面我們還能看出,我們要修改使用者資訊,完全用顯式地執行<code>session.update(user)</code>語句,隻需直接修改session緩存對象屬性即可,如下所示
我們資料庫中相應的user記錄name屬性也被修改了!
此外,session在清理緩存時,按照以下順序執行sql語句。
1。按照應用程式調用save()方法的先後順序,執行所有的對實體進行插入的insert語句。
2。所有對實體進行更新的update語句。
3。所有對實體進行删除的delete語句。
4。所有對集合元素進行删除、更新或插入的sql語句。
5。執行所有對集合進行插入的insert語句。
6。按照應用程式調用delete()方法的先後執行,執行所有對實體進行删除的delete語句。