Trying to destroy beans in the correct order with Spring

11,744

Solution 1

It seems to be that the order of destory method calls for non-singleton-scoped beans is completely out of control. From docs (5.1.4 Using depends-on):

The depends-on attribute in the bean definition can specify both an initialization time dependency and, in the case of singleton beans only, a corresponding destroy time dependency

You may create a helper object and delegate creation and destruction of your beans to it:

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" />

And, after all, perhaps it is not the best way to manage Hibernate sessions and transactions in Spring. Consider using of Spring's built-in Hibernate and transactions support.

EDIT: Well, the right way to manage transactions is:

  • You don't need request-scoped session and transaction beans
  • You shouldn't call createSession on the session factory returned by org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean. You can inject this session factory into your beans and call getCurrentSession when you need a session, a it will work fine.
  • You can use declarative transaction management (@Transactional annotations on the transactional methods). To make it work you should add to your config:

.

<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="hibernateSessionFactory"/>
</bean>

<tx:annotation-driven/>
  • For more information, see the links above

Solution 2

You could declare that hibernateTransaction depends-on hibernateSession. Since the container will instantiate beans in dependency order (barring cyclic dependencies), and tear them down in reverse dependency order, this should do the trick.

Solution 3

Transactions should be associated with services if you follow the Spring idiom. Sessions are web-tier objects, completely separate from the service tier. It sounds to me like you've made the mistake of entangling your web tier with the service tier. Better to tease them apart; you're unlikely to have this problem with that arrangement.

Share:
11,744
Chris
Author by

Chris

iOS developer / contractor based in Sydney.

Updated on June 19, 2022

Comments

  • Chris
    Chris almost 2 years

    I've got a web app with Spring set up to create my hibernate session factory (singleton) and session and transaction (both are request scoped), but it is destroying the session and transaction in the wrong order. How can i configure it so that the transaction is destroyed before the session? Here's my spring applicationContext.xml file:

    <?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>
    

    And here's the log that shows it closing the session before it closes the transaction:

    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