2013-08-23 5 views
6

(ancora un po 'nuova per primavera)Primavera: metodo @Transactional @Scheduled getta TransactionException

ho bisogno di avere un metodo di servizio che è allo stesso tempo @Scheduled e @Transactional, in modo che riesco a chiamare un DAO in esso.

Le transazioni dichiarative sono abilitate, il gestore transazioni è un org.springframework.orm.hibernate3.HibernateTransactionManager basato su una factory di sessione di sospensione.

La classe di servizio non implementa alcuna interfaccia in modo da utilizzare un proxy CGLIB.

Questa configurazione funziona in generale (metodi chiamati dallo stack Web, cioè Struts) ma questo metodo solleva un'eccezione quando viene chiamato dallo scheduler.

Ecco i bit rilevanti di codice:

Il metodo di servizio (la classe si chiama ClientWakeAndTerminateManager):

@Scheduled(initialDelay = 5000, fixedRateString = "${rmi.server.threads.clientsScheduleManagement.rate}") 
    @Transactional(readOnly = true) 
    public void runCheck(){ 

     //Call a read-only DAO method (the DAO is @Autowired as a class field) 

     //do some stuff with the data loaded from DB 

    } 

parti rilevanti del mio contesto di applicazione: pila

<!-- switch on the transactional infrastructure --> 
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> 

    <!-- Utility class to execute transactional code where use of annotation is not possible --> 
    <bean class="org.springframework.transaction.support.TransactionTemplate" id="txTemplate"> 
     <constructor-arg name="transactionManager" ref="transactionManager"/> 
    </bean> 

    <!-- Transaction manager based on Hibernate --> 
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
     <property name="sessionFactory" ref="hibernateSessionFactory"/> 
    </bean> 

Eccezione traccia:

[ERROR] : Unexpected error occurred in scheduled task. 
org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started 
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:661) 
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:755) 
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724) 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475) 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270) 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631) 
    at ch.unine.sitel.lis.rmi.shared.ClientWakeAndTerminateManager$$EnhancerByCGLIB$$d8be4f34.runCheck(<generated>) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:601) 
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64) 
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) 
    at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351) 
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) 
    at java.lang.Thread.run(Thread.java:722) 
Caused by: org.hibernate.TransactionException: Transaction not successfully started 
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:127) 
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:657) 
    ... 22 more 

La traccia dello stack sembra dirmi che un proxy transazionale è effettivamente utilizzato, quindi non capisco questa eccezione. Aiuto !

EDIT:

ho cercato di separare i @Transactional ei @Scheduled annotazioni da:

  • creare una nuova classe/bean che contaisn il metodo @Scheduled
  • Iniettare il mio servizio a questo fagiolo
  • Rimosso il @Scheduled dal metodo originale ma lasciato lo @Transactional

Ma ho ancora la stessa eccezione. Ho anche provato a mettere il @Transactional sul mio metodo DAO e rimuoverlo dal mio metodo di servizio: stesso risultato.

+0

questo può accadere quando si è già impegnata o rollback delle transazioni cerca di commit o rollback – wedens

+0

è possibile mostrare il metodo di dao? – wedens

+1

OK, il mio metodo DAO era il colpevole. Stavo iniziando a sospettare e poi il tuo commento mi ha fatto guardare due volte. Quindi il metodo conteneva alcuni commit di sessione manuali rimanenti (sto recentemente modernizzando e "innescando" questa app), quindi quando il proxy transazionale stava chiamando il commit falliva perché era già stato commesso! Grazie per il tuo tempo. Se modifichi la tua risposta per menzionare il metodo DAO, lo accetterò. –

risposta

11

creare una classe separata con il metodo annotato con @Transactional e chiamare questo metodo nel metodo annotato @Scheduled. primavera effettuerà una chiamata tramite proxy e gestirà correttamente @Transactional.

EDIT: guardare anche il metodo di DAO e assicurarsi che non commit o il rollback delle transazioni manualmente

+0

Grazie, ho fatto questo, ma ottengo lo stesso errore (vedi modifica) –

+0

Grazie @wedens per questa risposta, il suo lavoro per me. Puoi spiegare perché non funziona quando il metodo è nella stessa classe. – hayat

+0

Lo so, post molto vecchio ma odio lasciare la domanda sospesa. Il motivo è dovuto al modo in cui il proxy funziona. La risposta più semplice è che può vedere cosa sta entrando ed uscendo ma non ciò che sta accadendo all'interno. Ciò è dovuto alla natura di un proxy. – Wes