天天看點

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>