yml manager localcontainerentitymanagerfactorybean example entitymanagerfactory data configurar baeldung java spring spring-data-jpa entitymanager

java - manager - spring jpa configuration yml



¿Cómo iniciar manualmente una transacción en un EntityManager compartido en Spring? (3)

Tengo una instancia de LocalContainerEntityManagerFactoryBean como EntityManager .

Para eliminar rápidamente el contenido de una tabla completa, quiero ejecutar el siguiente código:

@Service public class DatabaseService { @Autowired private EntityManager em; @Transactional public void clear() { em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate(); } }

Resultado:

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)

Si hago este cambio:

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

Resultado:

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)

También probé spring-data-jpa, pero también falla:

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

Entonces, ¿cómo puedo crear una transacción y ejecutar el truncado en un contexto spring compartido?

La aplicación Spring se inicia utilizando: SpringApplication.run(AppConfig.class, args); teniendo:

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


Como solución alternativa, ahora creé un nuevo EntityManager explícito utilizando el EMF e iniciando la transacción 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(); }

Probablemente no sea lo ideal, pero funciona por el momento.


Debe usar el objeto TransactionTemplate para administrar la transacción de forma imperativa:

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

Para crear TransactionTemplate solo use PlatformTransactionManager inyectado:

transactionTemplate = new TransactionTemplate(platformTransactionManager);

Y si quieres usar una nueva transacción solo invoca

transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);


Spring Data JPA ejecuta automáticamente el método CRUD en las transacciones para usted (sin necesidad de configurar nada, excepto un administrador de transacciones). Si desea utilizar transacciones para sus métodos de consulta, simplemente puede agregar @Transactional a estos:

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

En una nota más general, lo que ha declarado aquí es lógicamente equivalente a CrudRepository.deleteAll() , excepto que (su declaración) no respeta las cascadas de nivel JPA. Así que me pregunté que eso es realmente lo que pretendías hacer. Si está utilizando Spring Boot, la configuración del administrador de activación y transacción debe ser atendida por usted.

Si desea utilizar @Transactional en el nivel de servicio, debe configurar un JpaTransactionManager y activar la gestión de transacciones basada en anotaciones a través de <tx:annotation-driven /> o @EnableTransactionManagement (parece que la activación fue la pieza faltante en su intento) para crear transacciones en la capa de servicio).