Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to destroy beans in the correct order with Spring

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
like image 575
Chris Avatar asked Jan 18 '10 00:01

Chris


2 Answers

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
like image 91
axtavt Avatar answered Sep 29 '22 09:09

axtavt


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.

like image 43
meriton Avatar answered Sep 29 '22 09:09

meriton