我有一个使用Spring设置的Web应用程序来创建我的hibernate会话工厂(单例)和会话和事务(两者都是请求作用域),但它正在以错误的顺序销毁会话和事务。我如何配置它以便在会话之前销毁事务?这是我的spring applicationContext.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="hibernateSessionFactory" scope="singleton"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
</bean>
<!-- The per-http request hibernate session -->
<bean id="hibernateSession" factory-bean="hibernateSessionFactory"
factory-method="openSession" destroy-method="close" scope="request" />
<!-- The per-http request transaction (i need this to be destroyed BEFORE the session) -->
<bean id="hibernateTransaction" factory-bean="hibernateSession"
factory-method="beginTransaction" destroy-method="commit" scope="request" />
</beans>
这是显示它在关闭事务之前关闭会话的日志:
16111 [http-8080-3] DEBUG org.springframework.beans.factory.support.DisposableBeanAdapter - Invoking destroy method 'close' on bean with name 'hibernateSession'
16111 [http-8080-3] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
16111 [http-8080-3] DEBUG com.mchange.v2.resourcepool.BasicResourcePool - trace com.mchange.v2.resourcepool.BasicResourcePool@17e4dee [managed: 4, unused: 3, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@19a8416)
16111 [http-8080-3] DEBUG org.springframework.beans.factory.support.DisposableBeanAdapter - Invoking destroy method 'commit' on bean with name 'hibernateTransaction'
16111 [http-8080-3] DEBUG org.hibernate.transaction.JDBCTransaction - commit
16111 [http-8080-3] WARN org.springframework.beans.factory.support.DisposableBeanAdapter - Invocation of destroy method 'commit' failed on bean with name 'hibernateTransaction'
org.hibernate.SessionException: Session is closed
答案 0 :(得分:4)
似乎是对非单例范围bean的destory方法调用的顺序完全失控。来自docs(5.1.4 Using depends-on):
bean定义中的depends-on属性可以指定初始化时间 依赖性和仅在单例bean的情况下,相应的销毁时间 依赖
您可以创建一个帮助对象,并将bean的创建和销毁委托给它:
public class HelperObject
{
private SessionFactory factory;
private Session session;
private Transaction tx;
public void init()
{
session = factory.createSession();
tx = session.beginTransaction();
}
public void destroy()
{
tx.commit();
session.close();
}
...
}
-
<bean id = "helperObject" class = "HelperObject" scope = "request" init-method = "init" destroy-method = "destroy">
<property name = "factory" ref = "hibernateSessionFactory" />
</bean>
<bean id="hibernateSession" factory-bean="helperObject"
factory-method="getSession" scope="request" />
<bean id="hibernateTransaction" factory-bean="helperObject"
factory-method="getTransaction" scope="request" />
毕竟,也许这不是在Spring中管理Hibernate会话和事务的最佳方式。考虑使用Spring的内置Hibernate和transactions支持。
修改强> 那么,管理交易的正确方法是:
session
和transaction
bean createSession
返回的会话工厂上调用org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean
。您可以将此会话工厂注入到bean中,并在需要会话时调用getCurrentSession
,它可以正常工作。@Transactional
注释)。要使它工作,您应该添加到您的配置:
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="hibernateSessionFactory"/>
</bean>
<tx:annotation-driven/>
答案 1 :(得分:1)
您可以声明hibernateTransaction
depends-on hibernateSession
。由于容器将按依赖顺序实例化bean(禁止循环依赖),并以反向依赖顺序将它们拆除,这应该可以解决问题。
答案 2 :(得分:1)
如果你遵循Spring惯用语,交易应该与服务相关联。会话是Web层对象,与服务层完全分开。对我来说听起来像你错误地将你的web层与服务层纠缠在了一起。最好将它们分开;你不太可能在这种安排下遇到这个问题。