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

在使用Hibernate时遇到LazyInitializationException,原因是尝试在Session关闭后访问延迟初始化的属性。延迟初始化允许集合在需要时才加载,但如果Session在访问前已关闭,会导致错误。解决方案包括在Session关闭前获取所有需要的属性,或者在映射文件中设置lazy="false"避免延迟初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值