2010-01-18 10 views
6

Tengo una aplicación web con Spring configurada para crear mi fábrica de sesiones de hibernación (singleton) y sesión y transacción (ambas tienen alcance de solicitud), pero está destruyendo la sesión y transacción en el orden incorrecto. ¿Cómo puedo configurarlo para que la transacción se destruya antes de la sesión? Aquí está mi archivo applicationContext.xml primavera:Intentando destruir frijoles en el orden correcto con Spring

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

Y aquí está el registro que muestra que el cierre de la sesión antes de que cierre la transacción:

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 [email protected] [managed: 4, unused: 3, excluded: 0] (e.g. [email protected]) 
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 

Respuesta

4

Parece ser que el orden de las llamadas al método de destino para los beans de ámbito no único está completamente fuera de control. A partir de documentos (3.4.3 Using depends-on):

El depende-de atributo en la definición de frijol se pueden especificar un tiempo de inicialización dependencia y, en el caso de los granos simples única, un destruyen correspondiente tiempo dependencia

es posible crear un objeto de ayuda y la creación de delegado y la destrucción de sus granos a ella:

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

Y, después de todo, tal vez no es la mejor manera de administrar las sesiones y transacciones de Hibernate en la primavera. Considere usar el soporte incorporado de Spring Hibernate y transactions.

EDIT: Bueno, la manera correcta de manejar transacciones es:

  • Usted no necesita la petición con ámbito de session y transaction granos
  • No debe llamar a la createSession fábrica de sesiones devuelta por org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean. Puede inyectar esta fábrica de sesión en sus granos y llamar al getCurrentSession cuando necesite una sesión, y funcionará bien.
  • Puede usar la gestión de transacciones declarativas (@Transactional anotaciones en los métodos transaccionales). Para hacerlo funcionar, debe agregar a su configuración:

.

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

<tx:annotation-driven/> 
  • Para obtener más información, ver los enlaces de arriba
+0

Hola, Tenía la sospecha de que la primavera tenía opciones integradas para administrar la sesión/txn, sin embargo, después de leer esos dos enlaces, todavía no estoy más cerca de entender cómo funcionarían. Creo que iré por su opción 'clase de ayuda', esa es una gran idea. Creo que es una lástima que la primavera no pueda controlar el orden de destrucción, realmente no está haciendo demasiado por mí. – Chris

+0

Miré esos 2 enlaces, y no puedo ver cómo podría usar el administrador de transacciones de primavera (o cualquier otra cosa) para darme una sesión y transacción que pudiera inyectar en mis acciones, solo parecía darme una sesión de fábrica que podría llamar a 'getCurrentSession()', que a mí no se ve bien. – Chris

+0

Supongo que lo que estoy preguntando es, si esta no es la mejor manera, ¿cuál es * la mejor manera de administrar sesiones/txns con la primavera? – Chris

1

Usted podría declarar que hibernateTransactiondepends-onhibernateSession. Dado que el contenedor creará instancias de beans en orden de dependencia (excluyendo dependencias cíclicas), y los desmantelará en orden de dependencia inverso, esto debería ser el truco.

+1

No funciona para beans de ámbito de solicitud. De acuerdo con la documentación, depends-on especifica la orden de destrucción solo para beans de ámbito singleton. – axtavt

+0

Sí, ya probé depende, no ayudó. Sin embargo, gracias por la respuesta! – Chris

1

transacciones deben ser asociados a los servicios si se sigue el lenguaje de primavera. Las sesiones son objetos de nivel web, completamente separados del nivel de servicio.Me parece que cometió el error de enredar su nivel web con el nivel de servicio. Es mejor separarlos; es poco probable que tengas este problema con ese arreglo.

+0

¡Me tomó media docena de lecturas de eso para descubrir lo que quería decir! Ahora asumo que cuando dices 'servicio' te refieres a las clases de capa 'de negocios' (por ejemplo, las clases que tienen cosas como 'FindEventById()'). Entonces, los servicios son los que se preocupan por las transacciones. Supongo que eso tiene sentido. – Chris

+0

Bien, he intentado con lo que sugirió, pero ahora tengo el problema de que mi nivel de servicios carga bien mi objeto de evento, pero cuando más adelante trato de acceder a los campos en el objeto devuelto, tiene una "LazyInitializationException - could not initialize proxy - sin sesión "error, presumiblemente porque la sesión se ha cerrado desde que mi acceso al nivel de servicios ha terminado. Entonces, creo que la sesión todavía tiene que estar vinculada a la solicitud web de alguna manera, ¿alguna sugerencia de cómo superó esto? – Chris

+0

Ah-ha! Funcionó: necesitaba OpenSessionInViewFilter configurado en el web.xml. ¡Uf! – Chris

Cuestiones relacionadas