您的位置:首页 > 其它

could not initialize proxy - the owning Session was closed

2010-12-04 22:33 537 查看
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>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">

</bean>

<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>


</beans>

Filter

的配置

<web-app>
< filter >
< filter-name > hibernateFilter </ filter-name >
< filter-class >
org.springframework.orm.hibernate.support.OpenSessionInViewFilter </ filter-class >
</ filter >

< filter-mapping >
< filter-name > hibernateFilter </ filter-name >
< url-pattern > *. spring </ url-pattern >
</ filter-mapping >

</web-app>


实现

Hibernate



Dao

接口来使用打开的会话是很容易的。事实上,如果你已经使用了

Spring

框架来实现你的

Hibernate Dao,

很可能你不需要改变任何东西。方便的

HibernateTemplate

公用组件使访问数据库变成小菜一碟,而

DAO

接口只有通过这个组件才可以访问到数据库。下面是一个示例的

DAO



public class HibernateProductDAO extends HibernateDaoSupport implements
ProductDAO {

public Product getProduct(Integer productId) {
return (Product ) getHibernateTemplate ().load(Product . class , productId);
}

public Integer saveProduct(Product product) {
return (Integer) getHibernateTemplate ().save(product);
}

public void updateProduct(Product product) {
getHibernateTemplate ().update(product);
}
}


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

即使在视图外面,

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

的方法拦截下来,再令它的方法支持延迟加载。看看下面的一个程序片段:

<beans>
<bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
<property name="someDAO"><ref bean="someDAO"/></property>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="businessObjectTarget"/></property>
<property name="proxyInterfaces">
<value>com.acompany.BusinessObject</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
</beans>




businessObject

被调用的时候,

HibernateInterceptor

打开一个

Hibernate

会话,并将调用请求传递给

BusinessObjectImpl

对象。当

BusinessObjectImpl

执行完成后,

HibernateInterceptor

透明地关闭了会话。应用层的代码不用了解任何持久层逻辑,还是实现了延迟加载。

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

最后,我们需要用

J-Unit

来测试我们的延迟加载程序。我们可以轻易地通过重写

TestCase

类中的

setUp



tearDown

方法来实现这个要求。我比较喜欢用这个方便的抽象类作为我所有测试类的基类。

import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

public abstract class MyLazyTestCase extends TestCase {

private SessionFactory sessionFactory ;
private Session session ;

public void setUp() throws Exception {
super .setUp();
SessionFactory sessionFactory = (SessionFactory) getBean( "sessionFactory" );
session = SessionFactoryUtils .getSession(sessionFactory, true );
Session s = sessionFactory.openSession();
TransactionSynchronizationManager .bindResource(sessionFactory, new SessionHolder (s));

}

protected Object getBean(String beanName) {
//Code to get objects from Spring application context
}

public void tearDown() throws Exception {
super .tearDown();
SessionHolder holder = (SessionHolder ) TransactionSynchronizationManager .getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager .unbindResource(sessionFactory);
SessionFactoryUtils .closeSessionIfNecessary(s, sessionFactory);
}

http://lz726.javaeye.com/blog/116616
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: