如何在Spring中的共享EntityManager上手动启动事务?

我有一个LocalContainerEntityManagerFactoryBean作为EntityManager实例。

要快速删除整个表的内容,我想运行以下代码:

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

结果:

 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) 

如果我做了这个改变:

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

结果:

 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) 

我也试过spring-data-jpa,但也失败了:

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

那么,我如何创建一个事务并在共享的spring上下文中运行truncate呢?

Spring应用程序使用以下SpringApplication.run(AppConfig.class, args);启动: SpringApplication.run(AppConfig.class, args); 有:

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

您应该使用TransactionTemplate对象来强制管理事务:

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

要创建TransactionTemplate,只需使用注入的PlatformTransactionManager

 transactionTemplate = new TransactionTemplate(platformTransactionManager); 

如果你想使用新的事务只需要调用

 transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 

作为一种解决方法,我现在使用EMF创建一个新的EntityManager ,并手动启动事务。

 @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(); } 

这可能不太理想,但暂时起作用。

Spring Data JPA会自动在事务中运行CRUD方法(无需设置除事务管理器之外的任何内容)。 如果要对查询方法使用事务,只需将@Transactional添加到这些:

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

更一般地说,你在这里声明的内容在逻辑上等同于CrudRepository.deleteAll() ,除了它(你的声明)不遵守JPA级别的级联。 所以我想知道这是你打算做的。 如果您使用的是Spring Boot,则应该为您处理激活和事务管理器设置。

如果要在服务级别使用@Transactional ,则需要通过@EnableTransactionManagement设置JpaTransactionManager 激活基于注释的事务管理(看起来激活是您尝试中缺失的部分)在服务层上创建事务)。