天天看点

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>