Hibernate 延遲加載html
主題java
概念web
集合屬性的延遲加載數據庫
關聯實體的延遲加載session
延遲加載的初始化錯誤框架
參考資料性能
概念spa
延遲加載是Hibernate爲提升程序執行效率而提供的一種機制,即只有真正須要數據的時候,才真正執行數據加載操做。hibernate
Hibernate中默認採用延遲加載的狀況主要有如下幾種線程
1.當調用session的load()方法加載一個實體時,會採用延遲加載;
2.實體的集合屬性默認延遲加載;
3.實體的關聯實體默認延遲加載;
延遲加載確實會給程序的查詢效率帶來好處,但有時明確知道數據須要當即加載,若是Hibernate先默認使用延遲加載,然後又必須去數據庫加載,反而會下降效率。
集合屬性的延遲加載
當Hibernate從數據庫中初始化某個持久化實體時,該實體的集合屬性是否隨持久化類一塊兒初始化呢?
若是集合屬性裏包含十萬,數百萬的記錄,在初始化持久化類的同時獲取集合屬性的值,將致使性能的降低。能夠沒必要要一次性加載全部的屬性,等系統須要集合屬性時才從數據庫加載關聯的數據。
此處有Person類和Address類,是一對多的關係;Person有集合屬性,是Address的集合。
Hibernate對集合屬性採起默認延遲加載,即 lazy=true;若訪問某ID的Person實體,Hibernate只會加載Person實體除集合屬性外的其餘屬性。這種作法能減小與數據庫的交互,避免里加載Address實體集合帶來的內存開銷。
加載Person實體時,Hibernate沒有從Address表中加載數據,這就是延遲加載。
那麼Hibernate在加載Person實體時,集合屬性值是什麼?從Eclipse的Variables窗口能夠看到
由上圖可知,集合屬性addresses不是HashSet,HashMap等集合的實現類,而是PersistentSet實現類,這是Hibernate爲Set接口提供的一個實現類。
PersistentSet沒有加載Address數據庫表的數據,裏面有個session屬性,這個session就是Hibernate的Session,當系統須要訪問集合屬性時,底層會經過PersistentSet裏的session屬性去加載與集合屬性關聯的數據庫記錄。
由上所知,Hibernate對於Set屬性延遲加載的關鍵是PersistentSet實現類。在延遲加載時,PersistentSet不包含任何記錄,可是有一個Hibernate Session屬性,當系統須要加載集合記錄時,可經過該session屬性從數據庫加載關聯的記錄。
除PersistentSet外,Hibernate 還提供了PersistentList,PersistentMap,PersistentSortedMap,PersistentSortedSet等實現類。
Hibernate要求聲明集合只能用Set,List,Map,SortedSet,SortedMap等接口,不能用HashSet,HashMap等接口的實現類,緣由就是Hibernate須要對屬性集合進行延遲加載,而Hibernate須要經過PersistentSet等Hibernate本身實現的接口實現類來完成延遲加載,所以Hibernate要求必須用集合接口,而非集合接口實現類來聲明集合屬性。
關聯實體的延遲加載
默認狀況下,Hibernate也會採用延遲加載來加載關聯實體。
這裏討論的關聯實體是單個實體,非集合形式的實體。
關聯單個實體時也是經過 lazy=true 來指定延遲加載。
此處經過Address類來討論關聯實體的延遲加載,Address有一個Person類型的實體屬性。在加載Address類的時候,從Eclipse的Variables窗口可看到
由上圖可知,Address所關聯的Person實體不是Person對象,而是一個Person_$$_javassist_0 類的實例。這個類是Hibernate使用Javassist項目生成的代理類。當Hibernate延遲加載關聯實體時,會採用Javassist生成動態代理類,這個代理對象負責代理暫未加載的關聯實體。
若是系統須要加載關聯實體,關聯實體的代理對象會負責去加載真正的關聯實體,並返回實際的關聯實體。
Hibernate採用延遲加載管理關聯實體,在加載實體時,並無獲取該實體的關聯實體對應的數據,只是動態生成一個關聯實體的代理對象。當系統須要使用關聯實體時,代理對象會負責從數據庫獲取記錄,並初始化真正的關聯實體。
代理對象具備查詢數據庫的能力。Hibernate經過CGLIB,來實現動態構造一個目標對象的代理對象,而且在代理對象中包含目標對象的全部方法和屬性,並且全部屬性被賦值爲null,真正的目標對象包含在代理對象的target屬性中。若是調用目標對象的方法獲取屬性值,經過CGLIB賦予的回調機制,實際調用的是代理對象的同名方法。當調用該方法時,Hibernate首先會檢查target屬性是否爲null,若是不爲null,則調用目標對象的對應方法。若是爲null,則會真正發起數據庫查詢,生成SQL語句查詢數據來構造目標對象,並將它賦值到target屬性中。可是若是系統訪問的是getId()方法,Hibernate不會初始化代理對象,由於在建立代理對象的時候ID就已經存在了,沒必要再去數據庫中查詢。
在Hibernate的延遲加載中,客戶的程序開始獲取的只是一個動態生成的代理對象,而真正的實體對象則委託給代理對象來管理-這就是典型的代理模式。
延遲加載的初始化錯誤
若是對一個集合屬性或者關聯實體配置了延遲加載,那麼代理對象或者代理集合處於持久化狀態,即處於session範圍內時,才能初始化它。若是初始化時session已經關閉,就會出現初始化錯誤。
Spring框架爲Hibernate延遲加載和DAO模式的整合提供了一種方便的解決方法。Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。這兩種方法實現的功能相同。不一樣點在於攔截器Interceptor在Spring容器中運行並被配置在web應用上下文中,過濾器Filter在Spring以前運行並配置在web.xml中。
OpenSessionInViewFilter是一個過濾器,用來把一個Hibernate Session和一次完整的請求過程對應的線程綁定。目的是爲了實現「Open Session in View」的模式。
參考資料
http://developer.51cto.com/art/200909/154624.htm
http://www.ibm.com/developerworks/cn/java/j-lo-hibernatelazy/
http://blog.sina.com.cn/s/blog_451f596201014zjp.html