2014-10-28 6 views
16

Ho un'istanza EntityManager come EntityManager.Come avviare manualmente una transazione su un EntityManager condiviso in primavera?

Per escludere rapidamente i contenuti a tavoli pieni, voglio eseguire il codice seguente:

@Service 
public class DatabaseService { 
    @Autowired 
    private EntityManager em; 

    @Transactional 
    public void clear() { 
     em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate(); 
    } 
} 

Risultato:

ERROR org.springframework.integration.handler.LoggingHandler: javax.persistence.TransactionRequiredException: Executing an update/delete query 
    at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:71) 
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) 
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:708) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) 
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644) 
    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:606) 
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) 
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) 
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304) 
    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:1145) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
    at java.lang.Thread.run(Thread.java:744) 

Se faccio questo cambiamento:

public void clear() { 
    em.getTransaction().begin(); 
    em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate(); 
} 

Risultato:

ERROR org.springframework.integration.handler.LoggingHandler: java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead 
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:245) 
    at com.sun.proxy.$Proxy84.getTransaction(Unknown Source) 
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) 
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:708) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) 
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644) 
    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:606) 
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) 
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) 
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304) 
    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:1145) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
    at java.lang.Thread.run(Thread.java:744) 

Ho anche provato la primavera-dati-JPA, ma fallisce anche:

public interface MyRepository extends CrudRepository<MyEntity, Integer> { 
    @Query(value = "TRUNCATE TABLE MyTable", nativeQuery = true) 
    @Modifying 
    public void clear(); 
} 

Quindi, come posso creare una transazione ed eseguire il troncamento in un contesto di primavera condiviso?

L'applicazione primavera è iniziata con: SpringApplication.run(AppConfig.class, args); avere:

@Bean 
public JpaTransactionManager transactionManager() { 
    return new JpaTransactionManager(emf); 
} 
+0

Si prega di inviare gli stacktrets completi inclusi causati da. – m3th0dman

+3

Non utilizzare '@ Autowired' per utilizzare' @ PersistenceContext'. –

+0

'@ PersistenceContext' non ha modificato nulla. Stacktrace aggiornato sopra. – membersound

risposta

25

Si dovrebbe usare TransactionTemplate oggetto per la gestione delle transazioni imperativamente:

transactionTemplate.execute(new TransactionCallbackWithoutResult() { 
     @Override 
     protected void doInTransactionWithoutResult(TransactionStatus status) { 
      em.createNativeQuery("TRUNCATE TABLE MyTable).executeUpdate(); 
     } 
    }); 

Per creare TransactionTemplate basta usare iniettato PlatformTransactionManager:

transactionTemplate = new TransactionTemplate(platformTransactionManager); 

E se si desidera utilizzare nuova transazione solo invocare

transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
2

Per aggirare il problema ho creato una nuova EntityManager esplicito utilizzando il EMF, e iniziare l'operazione manualmente.

@Autowired 
private EntityManagerFactory emf; 

public void clearTable() { 
    EntityManager em = emf.createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 
    tx.begin(); 
    em.createNativeQuery("TRUNCATE TABLE MyTable).executeUpdate(); 
    tx.commit(); 
    em.close(); 
} 

Probabilmente non è l'ideale, ma funziona per il momento.

+1

Non c'è alcun motivo per cui '@ Transactional' non dovrebbe funzionare, ci deve essere qualcosa di sbagliato nella configurazione. Inoltre entrambe le soluzioni sono soluzioni alternative invece di risolvere il problema. Una cosa che mi chiedo è che hai usato il corretto '@ Transactional 'quello di primavera e non quello più recente di JEE7? –

+1

Uso javax one – membersound

+0

E l'API nel classpath o solo come dipendenza fornita? Inoltre non stai usando le interfacce quindi assicurati di abilitare i proxy in classe ... Infine potresti aggiungere lo stacktrace completo al tuo post? –

3

Spring Data JPA esegue automaticamente il metodo CRUD nelle transazioni per te (senza bisogno di impostare nulla tranne un gestore delle transazioni). Se si desidera utilizzare le transazioni per i metodi di query, si può semplicemente aggiungere @Transactional a questi:

interface MyRepository extends CrudRepository<MyEntity, Integer> { 

    @Transactional 
    @Modifying 
    @Query(value = "TRUNCATE TABLE MyTable", nativeQuery = true) 
    void clear(); 
} 

Su una nota più generale, quello che hai dichiarato qui è logicamente equivalente a CrudRepository.deleteAll(), tranne che (la sua dichiarazione) non rispetta le cascate a livello JPA. Quindi mi sono chiesto che fosse davvero ciò che avevi intenzione di fare. Se stai usando Spring Boot, la configurazione di attivazione e gestione delle transazioni dovrebbe essere gestita per te.

Se si desidera utilizzare @Transactional sul livello di servizio, è necessario impostare sia un JpaTransactionManagere Attiva annotazioni basata gestione delle transazioni attraverso sia <tx:annotation-driven /> o @EnableTransactionManagement (sembra che l'attivazione è il pezzo mancante sul tentativo di creare transazioni sul livello di servizio).

+0

Penso che l'utilizzo di @transactional significhi che la molla gestirà automaticamente il commit e lo scarico. – obesechicken13

+0

Sospetto che la chiave non sia stata impostata in primavera. Fondamentalmente, Spring gestisce per te il gestore dell'entità (quindi non ti consente di gestire le tue transazioni), ma non hai detto come gestire le transazioni. –

Problemi correlati