在做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>