轉載:
HIBERNATE的持久化對象加載策略。
延遲加載, 也就是用到的時候纔去加載.這樣可以提高一些性能.
Hibernate的lazy loading 採用了一個HibernateSession來管理session,它的邏輯是每進行一次數據庫操作,就開新的session,操作完成後立即關閉該session。這樣做的好處是可以嚴格關閉session,避免菜鳥級的錯誤,但是hibernate.org並不推薦這麼做。因爲這不適合lazy loading,也不適合跨方法的事務。
比如在我們的應用中,user->post形成一對多的映射,User中有一個包含post的List。
在User中,有多個屬性:name,password,phone等,還有一個List類型的posts。當我們對posts使用lazy laoding的時候,hibernate會在獲得User對象的時候,僅僅返回name,password,phone等基本屬性,當你訪問posts的時候,它纔會從數據庫中提取posts需要的數據,這就是所謂lazy laoding。但是在我們的系統中,session是被立即關閉的,也就是在讀取了name,password,phone等基本屬性後,session已經close了,再進行lazy loaiding就會有異常。
解決辦法是在close session之前,調用Hibernate.initialize(user.getPosts()),告訴系統,user.getPosts()是需要lazy laoding的。但是這樣做會破壞HibernateSession類的封裝.
後來採用所謂的OpenSessionInView模式,把session的週期交給servlet filter來管理,每當有request進來,就打開一個session,response結束之後再關閉它,這樣可以讓session存在於整個請求週期中。
本文主要針對一對多情況下讀取父類的子集合時,hibernate 的lazy屬性在其中的影響進行總結。(以下代碼運行在jdk1.5,jboss eclipse ide 1.5,hibernate 3.1環境下) 假設有:父類 Person (含有Set類型屬性Address), 子類 Address(碰巧集合的名字和子類的名字都是Address,不要混淆了) Person.hbm.xml 主要片段: <id name="idx" column="idx" type="long" > <generator class="identity"> </generator> </id> <property name="age" type="int" update="true" insert="true" column="age" /> <property name="name" type="java.lang.String" update="true" insert="true" column="name" /> <set name="address" table="address" lazy="true" cascade="none" sort="unsorted" > <key > <column name="personidx" /> </key> <one-to-many class="com.abc.common.pojo.Address" />
</set> (1)在session 的週期內,無論lazy 設爲true or false, 不會有任何限制。訪問父子數據的代碼如下所示 : //打開session session = HibernateUtil.currentSession();
PersonDAO dao = new PersonDAO(); Person person = null;
person = (Person)dao.findByPrimaryKey(4); Set addressSet = person.getAddress();
Address[] addressAry = new Address[addressSet.size()]; Address address = null ; addressSet.toArray(addressAry); for(int i=0 ;i<addressAry.length;i++){ ................ } //session關閉 session.close(); if (session.isOpen()){ HibernateUtil.closeSession(); }
(2)在session的週期外,訪問父子數據的代碼如下所示 : //打開session session = HibernateUtil.currentSession();
PersonDAO dao = new PersonDAO(); Person person = null;
person = (Person)dao.findByPrimaryKey(4); /********************** *留待後續處理 *********************/ session.close(); //session關閉之後才訪問person的子集 Set addressSet = person.getAddress();
Address[] addressAry = new Address[addressSet.size()]; Address address = null ; addressSet.toArray(addressAry); for(int i=0 ;i<addressAry.length;i++){ ................ } if (session.isOpen()){ HibernateUtil.closeSession(); }
此時,上述代碼的運行結果根據lazy的設置的不同而不同 lazy=false 結果:可以訪問得到Person和Address的數據 lazy= true 根據代碼的寫法有不同 (1)代碼其他處不做任何處理,則拋出異常 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: 。。。。。。 (2)如果做一些處理如下,將上述那段代碼中的"留待後續處理"換成以下代碼 Hibernate.initialize(person.getAddress()); 則可以訪問得到Person和Address的數據 實際編寫時,不會象上述這樣的寫法,即將 Hibernate.initialize(person.getAddress());和person.getAddress()在同一個方法裏面調用。他們往往出現在應用程序的不同層次中(前者出現在DAO層居多,而後者則出現在web層居多). |