2010-01-18 4 views
6

J'ai une application Web avec Spring mis en place pour créer mon usine de session (singleton) hibernate et la session et la transaction (les deux sont portée par la demande), mais il détruit la session et transaction dans le mauvais ordre. Comment puis-je le configurer pour que la transaction soit détruite avant la session? Voici mon fichier applicationContext.xml printemps:Essayer de détruire les haricots dans le bon ordre avec le printemps

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

Et voici le journal qui le montre la fermeture de la session avant qu'elle ne ferme la 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 [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 

Répondre

4

Il semble que l'ordre des appels de méthode destory pour les beans non-singleton-scope est complètement hors de contrôle. De documents (3.4.3 Using depends-on):

La fonction sur l'attribut dans la définition de haricot peut indiquer à la fois une dépendance du temps d'initialisation et, dans le cas des haricots singleton seulement, l'un correspondant détruire temps dépendance

Vous pouvez créer un objet d'aide et à la création de délégués et la destruction de vos haricots à elle:

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

Et, après tout, il est peut-être pas la meilleure façon de gérer les sessions Hibernate et les transactions au printemps. Envisagez d'utiliser le support intégré Hibernate et transactions de Spring.

EDIT: Eh bien, la bonne façon de gérer les transactions est:

  • Vous n'avez pas besoin session et transaction demande scope haricots
  • Vous ne devez pas appeler createSession sur la usine de session renvoyée par org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean. Vous pouvez injecter cette usine de session dans vos haricots et appelez getCurrentSession lorsque vous avez besoin d'une session, il va fonctionner correctement.
  • Vous pouvez utiliser la gestion des transactions déclarative (@Transactional annotations sur les méthodes transactionnelles). Pour le faire fonctionner, vous devez ajouter à votre config:

.

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

<tx:annotation-driven/> 
  • Pour plus d'informations, voir les liens ci-dessus
+0

Salut, Je pensais que le printemps avait des options intégrées pour gérer la session/txn, mais après avoir lu ces deux liens, je ne suis toujours pas près de comprendre comment ils fonctionneraient. Je pense que je vais opter pour votre option «classe d'aide», c'est une excellente idée. Je pense qu'il est dommage que le printemps ne puisse pas contrôler l'ordre de destruction, il ne fait vraiment pas grand-chose pour moi. – Chris

+0

J'ai regardé ces 2 liens, et je ne peux pas voir comment je pourrais utiliser le gestionnaire de transaction de printemps (ou autre chose) pour me donner une session et une transaction que je pourrais injecter dans mes actions, cela semblait seulement me donner une session Je pourrais alors appeler 'getCurrentSession()', ce qui ne me semble pas très bien. – Chris

+0

Je suppose que ce que je demande est, si ce n'est pas la meilleure façon, quelle est la meilleure façon de gérer les sessions/txns avec le printemps? – Chris

1

Vous pouvez déclarer que hibernateTransactiondepends-onhibernateSession. Puisque le conteneur va instancier les beans dans l'ordre des dépendances (en excluant les dépendances cycliques), et les supprimer dans l'ordre inverse des dépendances, cela devrait faire l'affaire.

+1

Cela ne fonctionne pas pour les beans de portée requête. Selon la documentation, depend-on spécifie l'ordre de destruction uniquement pour les haricots à portée singleton. – axtavt

+0

Ouais, j'ai déjà essayé ça dépend, ça n'a pas aidé. Cependant, merci pour la réponse! – Chris

1

Les transactions devraient être associés à des services si vous suivez l'idiome de printemps. Les sessions sont des objets de niveau Web, complètement distincts du niveau de service.Il me semble que vous avez fait l'erreur d'emmêler votre niveau Web avec le niveau de service. Mieux vaut les démêler. il est peu probable que vous ayez ce problème avec cet arrangement.

+0

Il m'a fallu une demi-douzaine de lectures de cela pour comprendre ce que tu voulais dire! Je suppose maintenant que lorsque vous dites 'service' vous voulez dire les classes de couche 'business' (par exemple les classes qui ont des choses comme 'FindEventById()'). Les services sont donc ceux qui se soucient des transactions. Je suppose que cela a du sens. – Chris

+0

Okay j'ai essayé ce que vous avez suggéré, mais maintenant j'ai le problème que mon niveau de services charge bien mon objet événement, mais quand plus tard j'essaye d'accéder aux champs dans l'objet retourné, il a une "LazyInitializationException - ne peut pas initialiser proxy - pas de session ", vraisemblablement parce que la session s'est fermée depuis que mon accès au niveau des services est terminé. Donc, je pense que la session doit toujours être lié à la demande Web en quelque sorte, des suggestions comment vous avez surmonté cela? – Chris

+0

Ah-ha! Ça marche: OpenSessionInViewFilter nécessaire dans le fichier web.xml. Phew! – Chris

Questions connexes