could not initialize proxy - the owning Session was closed

could not initialize proxy - the owning Session was closed

关键字 : 异常引起的

 

其实这个异常写的非常之清楚,就是会话关闭,无法对 Hibernate 实体进行操作。造成这样的情况有很多,什么书写错误啊,逻辑错误啊 , 等等 . 不过 , 偶是因为 LAZY.

 

关于 lazy 机制:

延迟初始化错误是运用 Hibernate 开发项目时最常见的错误。如果对一个类或者集合配置了延迟检索策略,那么必须当代理类实例或代理集合处于持久化状态(即处于 Session 范围内)时,才能初始化它。如果在游离状态时才初始化它,就会产生延迟初始化错误。

 

下面把 Customer.hbm.xml 文件的 <class> 元素的 lazy 属性设为 true ,表示使用延迟检索策略:

 

    < class name = "mypack.Customer" table = "CUSTOMERS" lazy = "true" >

当执行 Session load() 方法时, Hibernate 不会立即执行查询 CUSTOMERS 表的 select 语句,仅仅返回 Customer 类的代理类的实例,这个代理类具由以下特征:

 

1 Hibernate 在运行时动态生成,它扩展了 Customer 类,因此它继承了 Customer 类的所有属性和方法,但它的实现对于应用程序是透明的。

2 Hibernate 创建 Customer 代理类实例时,仅仅初始化了它的 OID 属性,其他属性都为 null ,因此这个代理类实例占用的内存很少。

3 )当应用程序第一次访问 Customer 代理类实例时(例如调用 customer.getXXX() customer.setXXX() 方法), Hibernate 会初始化代理类实例,在初始化过程中执行 select 语句,真正从数据库中加载 Customer 对象的所有数据。但有个例外,那就是当应用程序访问 Customer 代理类实例的 getId() 方法时, Hibernate 不会初始化代理类实例,因为在创建代理类实例时 OID 就存在了,不必到数据库中去查询。

 

提示: Hibernate 采用 CGLIB 工具来生成持久化类的代理类。 CGLIB 是一个功能强大的 Java 字节码生成工具,它能够在程序运行时动态生成扩展 Java 类或者实现 Java 接口的代理类。关于 CGLIB 的更多知识,请参考: http://cglib.sourceforge.net/

 

以下代码先通过 Session load() 方法加载 Customer 对象,然后访问它的 name 属性:

 

       Transaction tx = session .beginTransaction();

       Customer customer=(Customer ) session .load(Customer . class , new Long(1));

       customer.getName();

       tx.commit();

在运行 session.load() 方法时 Hibernate 不执行任何 select 语句,仅仅返回 Customer 类的代理类的实例,它的 OID 1 ,这是由 load() 方法的第二个参数指定的。当应用程序调用 customer.getName() 方法时, Hibernate 会初始化 Customer 代理类实例,从数据库中加载 Customer 对象的数据,执行以下 select 语句:

 

select * from CUSTOMERS where ID=1;

select * from ORDERS where CUSTOMER_ID=1;

 

<class> 元素的 lazy 属性为 true ,会影响 Session load() 方法的各种运行时行为,下面举例说明。

 

1 .如果加载的 Customer 对象在数据库中不存在, Session load() 方法不会抛出异常,只有当运行 customer.getName() 方法时才会抛出以下异常:

 

ERROR LazyInitializer:63 - Exception initializing proxy

net.sf.hibernate.ObjectNotFoundException: No row with the given identifier exists: 1, of class:

mypack.Customer

 

2 .如果在整个 Session 范围内,应用程序没有访问过 Customer 对象,那么 Customer 代理类的实例一直不会被初始化, Hibernate 不会执行任何 select 语句。以下代码试图在关闭 Session 后访问 Customer 游离对象:

 

tx = session.beginTransaction();

Customer customer=(Customer)session.load(Customer.class,new Long(1));

tx.commit();

session.close();

customer.getName();

 

由于引用变量 customer 引用的 Customer 代理类的实例在 Session 范围内始终没有被初始化,因此在执行 customer.getName() 方法时, Hibernate 会抛出以下异常:

 

ERROR LazyInitializer:63 - Exception initializing proxy

net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed

 

由此可见, Customer 代理类的实例只有在当前 Session 范围内才能被初始化。

 

3 net.sf.hibernate.Hibernate 类的 initialize() 静态方法用于在 Session 范围内显式初始化代理类实例, isInitialized() 方法用于判断代理类实例是否已经被初始化。例如:

 

tx = session.beginTransaction();

Customer customer=(Customer)session.load(Customer.class,new Long(1));

if(!Hibernate.isInitialized(customer))

Hibernate.initialize(customer);

tx.commit();

session.close();

customer.getName();

 

以上代码在 Session 范围内通过 Hibernate 类的 initialize() 方法显式初始化了 Customer 代理类实例,因此当 Session 关闭后,可以正常访问 Customer 游离对象。

 

4 .当应用程序访问代理类实例的 getId() 方法时,不会触发 Hibernate 初始化代理类实例的行为,例如:

 

tx = session.beginTransaction();

Customer customer=(Customer)session.load(Customer.class,new Long(1));

customer.getId();

tx.commit();

session.close();

customer.getName();

 

当应用程序访问 customer.getId() 方法时,该方法直接返回 Customer 代理类实例的 OID 值,无需查询数据库。由于引用变量 customer 始终引用的是没有被初始化的 Customer 代理类实例,因此当 Session 关闭后再执行 customer.getName() 方法, Hibernate 会抛出以下异常:

 

ERROR LazyInitializer:63 - Exception initializing proxy

net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed

 

 

解决方法:

 

由于 hibernate 采用了 lazy=true, 这样当你用 hibernate 查询时 , 返回实际为利用 cglib 增强的代理类 , 但其并没有实际填充 ; 当你在前端 , 利用它来取值 (getXXX) , 这时 Hibernate 才会到数据库执行查询 , 并填充对象 , 但此时如果和这个代理类相关的 session 已关闭掉 , 就会产生种错误 .

在做一对多时,有时会出现 "could not initialize proxy - clothe owning Session was sed, 这个好像是 hibernate 的缓存问题 . 问题解决 : 需要在 <many-to-one> 里设置 lazy="false". 但有可能会引发另一个异常叫

 

failed to lazily initialize a collection of role: XXXXXXXX, no session or session was closed

 

此异常解决方案请察看本人博客( http://hi.baidu.com/kekemao1 )的 Hibernate 异常中的《 failed to lazily initialize a collection of role 异常》

 

?

解决方法 : web.xml 中加入

    < filter >

       < filter-name > hibernateFilter </ filter-name >

       < filter-class >

           org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </ filter-class >

    </ filter >

    < filter-mapping >

       < filter-name > hibernateFilter </ filter-name >

       < url-pattern > *.do </ url-pattern >

    </ filter-mapping > 就可以了 ;

 

参考了 :

Hibernate 与延迟加载:

 

Hibernate 对象关系映射提供延迟的与非延迟的对象初始化。非延迟加载在读取一个对象的时候会将与这个对象所有相关的其他对象一起读取出来。这有时会导致成百的(如果不是成千的话) select 语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,经常会导致整个数据库都在初始化的阶段被读出来了。当然,你可以不厌其烦地检查每一个对象与其他对象的关系,并把那些最昂贵的删除,但是到最后,我们可能会因此失去了本想在 ORM 工具中获得的便利。

 

 

一个明显的解决方法是使用 Hibernate 提供的延迟加载机制。这种初始化策略只在一个对象调用它的一对多或多对多关系时才将关系对象读取出来。这个过程对开发者来说是透明的,而且只进行了很少的数据库操作请求,因此会得到比较明显的性能提升。这项技术的一个缺陷是延迟加载技术要求一个 Hibernate 会话要在对象使用的时候一直开着。这会成为通过使用 DAO 模式将持久层抽象出来时的一个主要问题。为了将持久化机制完全地抽象出来,所有的数据库逻辑,包括打开或关闭会话,都不能在应用层出现。最常见的是,一些实现了简单接口的 DAO 实现类将数据库逻辑完全封装起来了。一种快速但是笨拙的解决方法是放弃 DAO 模式,将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系统中,这是一个严重的设计缺陷,妨碍了系统的可扩展性。

 

Web 层进行延迟加载

 

幸运的是, Spring 框架为 Hibernate 延迟加载与 DAO 模式的整合提供了一种方便的解决方法。对那些不熟悉 Spring Hibernate 集成使用的人,我不会在这里讨论过多的细节,但是我建议你去了解 Hibernate Spring 集成的数据访问。以一个 Web 应用为例, Spring 提供了 OpenSessionInViewFilter OpenSessionInViewInterceptor 。我们可以随意选择一个类来实现相同的功能。两种方法唯一的不同就在于 interceptor Spring 容器中运行并被配置在 web 应用的上下文中,而 Filter Spring 之前运行并被配置在 web.xml 中。不管用哪个,他们都在请求将当前会话与当前(数据库)线程绑定时打开 Hibernate 会话。一旦已绑定到线程,这个打开了的 Hibernate 会话可以在 DAO 实现类中透明地使用。这个会话会为延迟加载数据库中值对象的视图保持打开状态。一旦这个逻辑视图完成了, Hibernate 会话会在 Filter doFilter 方法或者 Interceptor postHandle 方法中被关闭。下面是每个组件的配置示例:

 

 

 

Interceptor 的配置 :

 

 

</beans>

 

Filter 的配置

 

 

 

 

实现 Hibernate Dao 接口来使用打开的会话是很容易的。事实上,如果你已经使用了 Spring 框架来实现你的 Hibernate Dao, 很可能你不需要改变任何东西。方便的 HibernateTemplate 公用组件使访问数据库变成小菜一碟,而 DAO 接口只有通过这个组件才可以访问到数据库。下面是一个示例的 DAO

 

 

 

在业务逻辑层中使用延迟加载

 

即使在视图外面, Spring 框架也通过使用 AOP 拦截器 HibernateInterceptor 来使得延迟加载变得很容易实现。这个 Hibernate 拦截器透明地将调用配置在 Spring 应用程序上下文中的业务对象中方法的请求拦截下来,在调用方法之前打开一个 Hibernate 会话,然后在方法执行完之后将会话关闭。让我们来看一个简单的例子,假设我们有一个接口 BussinessObject

 

 

public     interface    BusinessObject     {

public     void    doSomethingThatInvolvesDaos();

}

BusinessObjectImpl 实现了 BusinessObject 接口 :

 

public     class    BusinessObjectImpl    implements    BusinessObject     {

public     void    doSomethingThatInvolvesDaos()     {

//    lots of logic that calls

//    DAO classes Which access

//    data objects lazily

}

}

 

 

 

通过在 Spring 应用程序上下文中的一些配置,我们可以让将调用 BusinessObject 的方法拦截下来,再令它的方法支持延迟加载。看看下面的一个程序片段:

 

 

 

 

businessObject 被调用的时候, HibernateInterceptor 打开一个 Hibernate 会话,并将调用请求传递给 BusinessObjectImpl 对象。当 BusinessObjectImpl 执行完成后, HibernateInterceptor 透明地关闭了会话。应用层的代码不用了解任何持久层逻辑,还是实现了延迟加载。

 

 

在单元测试中测试延迟加载

 

最后,我们需要用 J-Unit 来测试我们的延迟加载程序。我们可以轻易地通过重写 TestCase 类中的 setUp tearDown 方法来实现这个要求。我比较喜欢用这个方便的抽象类作为我所有测试类的基类。

 

 

 

 


http://lz726.javaeye.com/blog/116616

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值