延迟初始化错误是q用Hibernate开发项目时最常见的错误。如果对一个类或者集合配|了延迟索策略,那么必须当代理类实例或代理集合处于持久化状态(卛_于Session范围内)Ӟ才能初始化它。如果在游离状态时才初始化它,׃产生延迟初始化错误?br />
下面把Customer.hbm.xml文g?lt;class>元素的lazy属性设为trueQ表CZ用gq检索策略:
<class name="mypack.Customer" table="CUSTOMERS" lazy="true">
当执行Session的load()ҎӞHibernate不会立即执行查询CUSTOMERS表的select语句Q仅仅返回Customercȝ代理cȝ实例Q这个代理类L以下特征Q?br />
Q?Q?由Hibernate在运行时动态生成,它扩展了Customerc,因此它承了Customercȝ所有属性和ҎQ但它的实现对于应用E序是透明的?br />Q?Q?当Hibernate创徏Customer代理cd例时Q仅仅初始化了它的OID属性,其他属性都为nullQ因此这个代理类实例占用的内存很?br />Q?Q当应用E序W一ơ访问Customer代理cd例时Q例如调用customer.getXXX()或customer.setXXX()ҎQ, Hibernate会初始化代理cd例,在初始化q程中执行select语句Q真正从数据库中加蝲Customer对象的所有数据。但有个例外Q那是当应用程序访问Customer代理cd例的getId()ҎӞHibernate不会初始化代理类实例Q因为在创徏代理cd例时OID存在了Q不必到数据库中L询?br />
提示QHibernate采用CGLIB工具来生成持久化cȝ代理cRCGLIB是一个功能强大的Java字节码生成工P它能够在E序q行时动态生成扩?JavacL者实现Java接口的代理类。关于CGLIB的更多知识,请参考:http://cglib.sourceforge.net/?br />
以下代码先通过Session的load()Ҏ加蝲Customer对象Q然后访问它的name属性:
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
customer.getName();
tx.commit();
在运行session.load()Ҏ时Hibernate不执行Q何select语句Q仅仅返回Customercȝ代理cȝ实例Q它的OID?Q这是由load()Ҏ的第二个参数指定的。当应用E序调用customer.getName()ҎӞHibernate会初始化Customer代理cd例,从数据库中加载Customer对象的数据,执行以下select语句Q?br />
select * from CUSTOMERS where ID=1;
select * from ORDERS where CUSTOMER_ID=1;
?lt;class>元素的lazy属性ؓtrueQ会影响Session的load()Ҏ的各U运行时行ؓQ下面D例说明?br />
1Q如果加载的Customer对象在数据库中不存在QSession的load()Ҏ不会抛出异常Q只有当q行customer.getName()Ҏ时才会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.ObjectNotFoundException: No row with the given identifier exists: 1, of class:
mypack.Customer
2Q如果在整个Session范围内,应用E序没有讉KqCustomer对象Q那么Customer代理cȝ实例一直不会被初始化,Hibernate不会执行Mselect语句。以下代码试囑֜关闭Session后访问Customer游离对象Q?br />
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
tx.commit();
session.close();
customer.getName();
׃引用变量customer引用的Customer代理cȝ实例在Session范围内始l没有被初始化,因此在执行customer.getName()ҎӞHibernate会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed
由此可见QCustomer代理cȝ实例只有在当前Session范围内才能被初始化?br />
3Qnet.sf.hibernate.Hibernatecȝinitialize()静态方法用于在Session范围内显式初始化代理cd例,isInitialized()Ҏ用于判断代理cd例是否已l被初始化。例如:
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范围内通过Hibernatecȝinitialize()Ҏ昑ּ初始化了Customer代理cd例,因此当Session关闭后,可以正常讉KCustomer游离对象?br />
4Q当应用E序讉K代理cd例的getId()ҎӞ不会触发Hibernate初始化代理类实例的行为,例如Q?br />
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
customer.getId();
tx.commit();
session.close();
customer.getName();
当应用程序访问customer.getId()ҎӞ该方法直接返回Customer代理cd例的OID|无需查询数据库。由于引用变?customer始终引用的是没有被初始化的Customer代理cd例,因此当Session关闭后再执行customer.getName()ҎQ?Hibernate会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed
解决ҎQ?br />
׃hibernate采用了lazy=true,q样当你用hibernate查询?q回实际为利用cglib增强的代理类,但其q没有实际填?当你在前?利用它来取?getXXX)?q时Hibernate才会到数据库执行查询,q填充对?但此时如果和q个代理cȝ关的session已关闭掉,׃产生U错?
在做一对多Ӟ有时会出?could not initialize proxy - clothe owning Session was sed,q个好像是hibernate的缓存问?问题解决:需要在<many-to-one>里设|lazy="false". 但有可能会引发另一个异常叫
failed to lazily initialize a collection of role: XXXXXXXX, no session or session was closed
此异常解x案请察看本h博客Qhttp://hi.baidu.com/kekemao1Q的Hibernate异常中的《failed to lazily initialize a collection of role异常?br />
?
解决Ҏ:在web.xml中加?br /><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与gq加载:
Hibernate对象关系映射提供延迟的与非gq的对象初始化。非延迟加蝲在读取一个对象的时候会与q个对象所有相关的其他对象一赯取出来。这有时会导致成癄Q如果不是成千的话)select语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,l常会导致整个数据库都在初始化的阶段被读出来了。当Ӟ你可以不厌其烦地查每一个对象与其他对象的关p,q把那些最昂贵的删除,但是到最后,我们可能会因此失M本想在ORM工具中获得的便利?br />
一个明昄解决Ҏ是用Hibernate提供的gq加载机制。这U初始化{略只在一个对象调用它的一对多或多对多关系时才关pd象读取出来。这个过E对开发者来说是透明的,而且只进行了很少的数据库操作hQ因此会得到比较明显的性能提升。这Ҏ术的一个缺h延迟加蝲技术要求一个Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式持久层抽象出来时的一个主要问题。ؓ了将持久化机制完全地抽象出来Q所有的数据库逻辑Q包括打开或关闭会话,都不能在应用层出现。最常见的是Q一些实C单接口的DAO实现cd数据库逻辑完全装h了。一U快速但是笨拙的解决Ҏ是放弃DAO模式Q将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系l中Q这是一个严重的设计~陷Q妨了pȝ的可扩展性?br />
在Web层进行gq加?br />
q运的是QSpring框架为Hibernate延迟加蝲与DAO模式的整合提供了一U方便的解决Ҏ。对那些不熟悉Spring与Hibernate集成使用的hQ我不会在这里讨多的l节Q但是我你去了解Hibernate与Spring集成的数据访问。以一个Web应用ZQSpring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择一个类来实现相同的功能。两U方法唯一的不同就在于interceptor在Spring容器中运行ƈ被配|在web应用的上下文中,而Filter在Spring之前q行q被配置在web.xml中。不用哪个Q他们都在请求将当前会话与当前(数据库)U程l定时打开Hibernate会话。一旦已l定到线E,q个打开了的Hibernate会话可以在DAO实现cM透明C用。这个会话会为gq加载数据库中值对象的视图保持打开状态。一旦这个逻辑视图完成了,Hibernate会话会在Filter的doFilterҎ或者Interceptor的postHandleҎ中被关闭。下面是每个lg的配|示例:
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的配|?br />
<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接口来用打开的会话是很容易的。事实上Q如果你已经使用了Spring框架来实C的Hibernate Dao,很可能你不需要改变Q何东ѝ方便的HibernateTemplate公用lg使访问数据库变成菜一,而DAO接口只有通过q个lg才可以访问到数据库。下面是一个示例的DAOQ?br />
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来得gq加载变得很Ҏ实现。这个Hibernate 拦截器透明地将调用配置在Spring应用E序上下文中的业务对象中Ҏ的请求拦截下来,在调用方法之前打开一个Hibernate会话Q然后在Ҏ执行完之后将会话关闭。让我们来看一个简单的例子Q假设我们有一个接口BussinessObjectQ?br />
public interface BusinessObject {
public void doSomethingThatInvolvesDaos();
}
cBusinessObjectImpl实现了BusinessObject接口:
public class BusinessObjectImpl implements BusinessObject {
public void doSomethingThatInvolvesDaos() {
// lots of logic that calls
// DAO classes Which access
// data objects lazily
}
}
通过在Spring应用E序上下文中的一些配|,我们可以让将调用BusinessObject的方法拦截下来,再o它的Ҏ支持延迟加蝲。看看下面的一个程序片D:
<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会话Qƈ调用请求传递给BusinessObjectImpl对象。当BusinessObjectImpl执行完成后,HibernateInterceptor透明地关闭了会话。应用层的代码不用了解Q何持久层逻辑Q还是实C延迟加蝲?br />
在单元测试中试延迟加蝲
最后,我们需要用J-Unit来测试我们的延迟加蝲E序。我们可以轻易地通过重写TestCasecM的setUp和tearDownҎ来实现这个要求。我比较喜欢用这个方便的抽象cM为我所有测试类的基cR?br />
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);
}
--------------------------------------------------------------
--------------------------------------------------------------
- <?xml version="1.0"?>
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mapping package="com.isoftstone.lms.model">
- <class name="Order" table="sv_order" >
- <id name="orderId" column="sv_order_id" type="string">
- <generator class="sequence">
- <param name="sequence">SEQ_ORDERID</param>
- </generator>
- </id>
-
- many-to-one name="style" column="sv_style_id" lazy="false" />
- <many-to-one name="state" column="sv_state_id" lazy="false"/>
- <many-to-one name="client" column="sv_client_id" lazy="false" />
- <many-to-one name="baseOrder" column="sv_order_baseid" lazy="false"/>
- <property name="orderNo" column="sv_order_orderno" type="long" />
- <property name="createDate" column="sv_order_createdate" type="date" />
- <property name="sendDate" column="sv_order_senddate" type="string" />
- <property name="sendAddress" column="sv_order_sendAddress" type="string" />
- <property name="accepter" column="sv_order_accepter" type="string" />
- <property name="postNo" column="sv_order_postNo" type="string" />
- <property name="phone" column="sv_order_phone" type="string" />
- <property name="totalMoney" column="sv_order_totalmoney" type="double" />
- <property name="isinvoice" column="sv_order_isinvoice" type="int" />
- <property name="remark" column="sv_order_remark" type="string" />
-
- <set name="orderGoodss" inverse="true" lazy="extra" cascade="all">
- <key column="sv_order_id"/>
- <one-to-many class="OrderGoods"/>
- </set>
- </class>
- </hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.isoftstone.lms.model">
<class name="Order" table="sv_order" >
<id name="orderId" column="sv_order_id" type="string">
<generator class="sequence">
<param name="sequence">SEQ_ORDERID</param>
</generator>
</id>
many-to-one name="style" column="sv_style_id" lazy="false" />
<many-to-one name="state" column="sv_state_id" lazy="false"/>
<many-to-one name="client" column="sv_client_id" lazy="false" />
<many-to-one name="baseOrder" column="sv_order_baseid" lazy="false"/>
<property name="orderNo" column="sv_order_orderno" type="long" />
<property name="createDate" column="sv_order_createdate" type="date" />
<property name="sendDate" column="sv_order_senddate" type="string" />
<property name="sendAddress" column="sv_order_sendAddress" type="string" />
<property name="accepter" column="sv_order_accepter" type="string" />
<property name="postNo" column="sv_order_postNo" type="string" />
<property name="phone" column="sv_order_phone" type="string" />
<property name="totalMoney" column="sv_order_totalmoney" type="double" />
<property name="isinvoice" column="sv_order_isinvoice" type="int" />
<property name="remark" column="sv_order_remark" type="string" />
<set name="orderGoodss" inverse="true" lazy="extra" cascade="all">
<key column="sv_order_id"/>
<one-to-many class="OrderGoods"/>
</set>
</class>
</hibernate-mapping>
[color=red]- <many-to-one name="style" column="sv_style_id" lazy="false" />
- <many-to-one name="state" column="sv_state_id" lazy="false"/>
- <many-to-one name="client" column="sv_client_id" lazy="false" />
- <many-to-one name="baseOrder" column="sv_order_baseid" lazy="false"/>
<many-to-one name="style" column="sv_style_id" lazy="false" />
<many-to-one name="state" column="sv_state_id" lazy="false"/>
<many-to-one name="client" column="sv_client_id" lazy="false" />
<many-to-one name="baseOrder" column="sv_order_baseid" lazy="false"/>
[/color]
]]>