2010-01-18 12 views
6

Ho un'app Web con Spring set up per creare la mia factory di sessione di sospensione (singleton) e sessione e transazione (entrambi sono scope scope), ma sta distruggendo la sessione e transazione nell'ordine sbagliato. Come posso configurarlo in modo che la transazione venga distrutta prima della sessione? Ecco la mia primavera del file applicationContext.xml:Cercando di distruggere i bean nell'ordine corretto 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> 

Ed ecco il log che mostra la chiusura della sessione prima di chiudere la transazione:

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 

risposta

4

Sembra che l'ordine del metodo destory richiami i bean con ambito non singleton sia completamente fuori controllo. Da documenti (3.4.3 Using depends-on):

L'dipende-on attributo nella definizione bean può specificare sia un tempo di inizializzazione dipendenza e, nel caso di fagioli Singleton solo, un corrispondente distruggere tempo dipendenza

si può creare un oggetto di supporto e la creazione delegato e la distruzione dei vostri fagioli ad esso:

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

E, dopo tutto, forse non è il modo migliore per gestire le sessioni Hibernate e transazioni in primavera. Prendi in considerazione l'utilizzo del supporto integrato Spring Hibernate e transactions.

EDIT: Beh, il modo giusto per gestire le transazioni è:

  • Non è necessario richiesta con ambito session e transaction fagioli
  • Non si dovrebbe chiamare createSession sulla la fabbrica di sessione restituita da org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean. È possibile inserire questa factory di sessione nei propri bean e chiamare lo getCurrentSession quando è necessaria una sessione, a funzionerà correttamente.
  • È possibile utilizzare la gestione dichiarativa delle transazioni (annotazioni @Transactional nei metodi transazionali). Per farlo funzionare devi aggiungere alla tua configurazione:

.

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

<tx:annotation-driven/> 
  • Per ulteriori informazioni, vedere i link qui sopra
+0

Ciao, Avevo il sospetto che la primavera avesse opzioni integrate per la gestione della sessione/txn, tuttavia dopo aver letto quei due collegamenti non sono ancora più vicino a capire come funzionerebbero. Penso che andrò per la tua opzione 'helper class', è una grande idea. Penso che sia un peccato che la primavera non possa controllare l'ordine di distruggere, in realtà non sta facendo molto per me. – Chris

+0

Ho guardato quei 2 link e non riesco a vedere come potrei usare il gestore delle transazioni di primavera (o qualsiasi altra cosa) per darmi una sessione e una transazione che potrei iniettare nelle mie azioni, sembrava solo darmi una sessione fabbrica che potrei quindi chiamare 'getCurrentSession()', che per me non sembra carino. – Chris

+0

Credo che quello che sto chiedendo sia, se questo non è il modo migliore, quale * è * il modo migliore per gestire sessioni/txns con la primavera? – Chris

1

Si potrebbe dichiarare che hibernateTransactiondepends-onhibernateSession. Poiché il contenitore istanzia i bean in ordine di dipendenza (escludendo le dipendenze cicliche) e li abbatte in ordine di dipendenza inversa, questo dovrebbe fare il trucco.

+1

Non funziona per i bean con ambito di richiesta. Secondo la documentazione, dipende-specifica l'ordine di distruzione solo per i bean con scope singleton. – axtavt

+0

Sì, ho già provato a dipendere, non ha aiutato. Tuttavia, grazie per la risposta! – Chris

1

transazioni devono essere associati ai servizi se si segue l'idioma di primavera. Le sessioni sono oggetti di livello Web, completamente separati dal livello di servizio.Mi sembra che tu abbia commesso l'errore di impigliare il tuo livello web con il livello di servizio. Meglio metterli a parte; è improbabile che abbia questo problema con questo accordo.

+0

Mi ci sono voluto mezza dozzina di letture per capire cosa intendevi! Ora suppongo che quando dici "servizio" intendi le classi di livello "business" (ad es. Le classi che contengono cose come "FindEventById()"). Quindi i servizi sono quelli che si preoccupano delle transazioni. Immagino che abbia senso. – Chris

+0

Okay ho provato quello che hai suggerito, ma ora ho il problema che il mio livello di servizi carica il mio oggetto evento, ma quando più tardi cerco di accedere ai campi nell'oggetto restituito, ha un "LazyInitializationException - non può inizializzarsi "proxy - nessuna sessione", presumibilmente perché la sessione è chiusa da quando il mio accesso al livello dei servizi è terminato. Quindi penso che la sessione debba ancora essere legata alla richiesta web in qualche modo, a qualche suggerimento su come hai superato questo? – Chris

+0

Ah-ha! Funzionante: necessario OpenSessionInViewFilter impostato nel web.xml. Accidenti! – Chris

Problemi correlati