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

mem*_*und 26 java spring entitymanager spring-data-jpa

我有一个LocalContainerEntityManagerFactoryBeanEntityManager实例.

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

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

    @Transactional
    public void clear() {
        em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate();
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

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)
Run Code Online (Sandbox Code Playgroud)

如果我做了这个改变:

public void clear() {
    em.getTransaction().begin();
    em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate();
}
Run Code Online (Sandbox Code Playgroud)

结果:

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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

@Bean
public JpaTransactionManager transactionManager() {
    return new JpaTransactionManager(emf);
}
Run Code Online (Sandbox Code Playgroud)

Jak*_*ski 46

您应该使用TransactionTemplateobject来强制管理事务:

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

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

transactionTemplate = new TransactionTemplate(platformTransactionManager);
Run Code Online (Sandbox Code Playgroud)

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

transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
Run Code Online (Sandbox Code Playgroud)


mem*_*und 13

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

@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();
}
Run Code Online (Sandbox Code Playgroud)

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

  • "暂时工作"可能是软件工程中最常用的句子 (3认同)
  • 没有理由说"@ Transactional"不起作用,你的设置一定有问题.这两种解决方案都是解决方法,而不是实际解决问题.有一件事我想知道你是从春天使用正确的`@Traffical`而不是JEE7的新版本? (2认同)

Oli*_*ohm 7

Spring Data JPA会自动为您执行事务中的CRUD方法(无需设置除事务管理器之外的任何内容).如果要对查询方法使用事务,只需添加@Transactional以下内容:

interface MyRepository extends CrudRepository<MyEntity, Integer> {

  @Transactional
  @Modifying
  @Query(value = "TRUNCATE TABLE MyTable", nativeQuery = true)
  void clear();
}
Run Code Online (Sandbox Code Playgroud)

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

如果你想使用@Transactional的服务水平,你需要设置既JpaTransactionManager 通过任何激活基于注解的事务管理<tx:annotation-driven />@EnableTransactionManagement(貌似激活是在你尝试创建服务层上的交易缺少的部分).