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

goucqfw6  于 2022-11-10  发布在  Spring
关注(0)|答案(4)|浏览(195)

我有一个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<MyEntity, Integer> {
    @Query(value = "TRUNCATE TABLE MyTable", nativeQuery = true)
    @Modifying
    public void clear();
}

那么,如何创建事务并在共享Spring上下文中运行truncate呢?
Spring应用程序是使用以下命令启动的:SpringApplication.run(AppConfig.class, args);,其具有:

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

ssgvzors1#

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

transactionTemplate.execute(
    status -> em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate());

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

transactionTemplate = new TransactionTemplate(platformTransactionManager);

如果要使用新事务,只需调用

transactionTemplate.setPropagationBehavior(
    TransactionDefinition.PROPAGATION_REQUIRES_NEW);
yeotifhr

yeotifhr2#

作为一种解决方法,我现在使用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();
}

这可能不太理想,但暂时可行。

y4ekin9u

y4ekin9u3#

Spring Data JPA会自动为您运行事务中的CRUD方法(除了事务管理器之外,无需设置任何东西)。如果您希望将事务用于查询方法,只需将@Transactional添加到以下方法中:

interface MyRepository extends CrudRepository<MyEntity, Integer> {

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

更一般地说,您在这里声明的内容在逻辑上等价于CrudRepository.deleteAll(),只是它(您的声明)不荣誉JPA级级联。因此,我想知道这是否是您真正想要做的。如果您使用的是Sping Boot ,那么激活和事务管理器设置应该为您处理。
如果您希望在服务层上使用@Transactional,则需要通过<tx:annotation-driven />@EnableTransactionManagement设置基于JpaTransactionManager * 和 * 激活注解的事务管理(看起来激活是您尝试在服务层上创建事务时缺少的部分)。

quhf5bfb

quhf5bfb4#

@Transactional注解不应该套用在Dao方法上,而应该套用在服务方法上。虽然您的程式码说DatabaseService是服务,但是在服务内插入EntityManger并没有任何意义。
正确的做法是创建一个刀像下面。

@Repository
public class DatabaseDao {
    @PersistenceContext
    private EntityManager em;

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

然后从带有@Transactional注解的服务方法调用dao方法。

@Service
public class DatabaseService {
    @Autowired
    private DatabaseDao dao;

    @Transactional
    public void clear() {
        dao.clear();
    }
}

此外,在Configuration类中添加@EnableTransactionManagement

相关问题