Hibernate 中的 延遲初始化……Lazy Initialization

在做hibernate讀取數據練習的時候,發現一個奇怪的事情:

AdminDAOFactory類中代碼如下:
public Admin load(int adminId) throws HibernateException {
   session = HibernateSessionFactory.currentSession();
   tx = session.beginTransaction(); /**
    try {
    
     //load admin
     Admin admin=(Admin)session.load(Admin.class,new Integer(adminId));
//     System.out.println("Id=" + admin.getId());
//     System.out.println("username=" + admin.getUsername());
     tx.commit ();
     return admin;
    }catch(HibernateException e){
     throw e;
    }finally{
     if (tx!=null) {
      tx.rollback();
     }
     HibernateSessionFactory.closeSession();
    }
   }

jsp頁面代碼:
     Admin user = new Admin();
     AdminDAOFactory dao = new AdminDAOFactory();
     user = dao.load(5);
     out.println("id = " + user.getId() + "<br>");
     out.println("username = " + user.getUsername() + "<br>");

現象:
運行到 jsp頁面顯示 user.getUsername()方法的時候報錯:
exception
org.apache.jasper.JasperException: could not initialize proxy - the owning Session was closed
root cause
org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed

臨時解決方法:
   (1)在return admin代碼之前,先執行admin.getUsername()方法。
   (2)將HibernateSessionFactory.closeSession();這句屏蔽。
   這兩種方法使用任何一種都能避免錯誤產生。但這樣很不正常,如果使用第一種解決方案那麼當admin對象中有n多個屬性時,將要預先執行n多遍的get方法;如果使用第二種方法,將不能及時關閉session對象,容易導致系統資源的大量浪費。到底應該如何正確解決這問題呢? 到網上搜索了一番,得知了Hibernate的延遲初始化……Lazy Initialization。

在Hibernate 3.0的中文參考手冊中有如下內容:
------------------------------
6.5. 延遲初始化(延遲加載)(Lazy Initialization)
(譯者注: 本翻譯稿中,對Lazy Initiazation和Eager fetch中的lazy,eager採取意譯的方式,分別翻譯爲延遲初始化和預先抓取。lazt initiazation就是指直到第一次調用時才加載。)
集合(不包括數組)是可以延遲初始化的,意思是僅僅當應用程序需要訪問時,才載入他們的值。對於使用者來說,初始化是透明的, 因此應用程序通常不需要關心這個(事實上,透明的延遲加載也就是爲什麼Hibernate需要自己的集合實現的主要原因)。但是, 如何應用程序試圖執行以下程序:
s = sessions.openSession();
User u = (User) s.find("from User u where u.name=?", userName, Hibernate.STRING).get(0);
Map permissions = u.getPermissions();
s.connection().commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts");   // Error!
這個錯誤可能令你感到意外。因爲在這個Session被提交(commit)之前, permissions沒有被初始化,那麼這個集合將永遠不能載入他的數據了。 解決方法是把讀取集合數據的語句提到Session被提交之前。(然而,還有一種更先進的方法來解決這個問題。)
另外一種選擇是不使用延遲初始化集合。既然延遲初始化可能引起上面這樣錯誤,默認是不使用延遲初始化的。但是, 爲了效率的原因, 我們希望對絕大多數集合(特別是實體集合)使用延遲初始化。
------------------------------

   hb3對映射集合的默認處理是lazy = "proxy";也就是在load之後,admin對象內的屬性並沒有被負值,只有當該屬性被jsp頁面使用時臨時從數據庫讀取,但該session已經被關閉,所以才導致出錯。
   前兩種臨時解決方案之所以能避免報錯主要原因是:
   (1)第一種方法,在session關閉之前讀取屬性值,從而能夠正常從數據庫中讀出,同時admin對象已經被持久化,故錯誤消失
   (2)第二種方法,不關閉session,在jsp頁面使用get方法的時候,能夠通過session從數據庫讀取屬性值,故錯誤消失

   經過查閱資料,只需要在hibernate的配置文件中增加 lazy="false",就可以妥善的解決這個問題了! 配置文件舉例:
     <class name="Admin" table="admin" lazy="false">
         <id name="id" column="id" type="integer">
             <generator class="native"/>
         </id>

         <property name="username" column="userName" type="string" />
         <property name="password" column="password" type="string" />
     </class>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章