尝试使用Spring以正确的顺序销毁bean

时间:2010-01-18 00:42:03

标签: java hibernate spring struts2

我有一个使用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

3 个答案:

答案 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的内置Hibernatetransactions支持。

修改 那么,管理交易的正确方法是

  • 您不需要请求范围的sessiontransaction 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层与服务层纠缠在了一起。最好将它们分开;你不太可能在这种安排下遇到这个问题。