Java 如何在 Spring 中的共享 EntityManager 上手动启动事务?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26606608/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to manually start a transaction on a shared EntityManager in Spring?
提问by membersound
I have a LocalContainerEntityManagerFactoryBean
as EntityManager
instance.
我有一个LocalContainerEntityManagerFactoryBean
作为EntityManager
实例。
To quickly drop a full tables' content, I want to run the following code:
要快速删除完整表格的内容,我想运行以下代码:
@Service
public class DatabaseService {
@Autowired
private EntityManager em;
@Transactional
public void clear() {
em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate();
}
}
Result:
结果:
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.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.access1(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)
If I make this change:
如果我进行此更改:
public void clear() {
em.getTransaction().begin();
em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate();
}
Result:
结果:
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.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.access1(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)
I also tried spring-data-jpa, but also fails:
我也试过 spring-data-jpa,但也失败了:
public interface MyRepository extends CrudRepository<MyEntity, Integer> {
@Query(value = "TRUNCATE TABLE MyTable", nativeQuery = true)
@Modifying
public void clear();
}
So, how can I create a transaction and run the truncate in a shared spring context?
那么,如何创建事务并在共享的 spring 上下文中运行截断?
The Spring application is started using:
SpringApplication.run(AppConfig.class, args);
having:
Spring 应用程序启动时使用:
SpringApplication.run(AppConfig.class, args);
具有:
@Bean
public JpaTransactionManager transactionManager() {
return new JpaTransactionManager(emf);
}
采纳答案by Jakub Kubrynski
You should use TransactionTemplate
object to manage transaction imperatively:
您应该使用TransactionTemplate
object 来强制管理事务:
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
em.createNativeQuery("TRUNCATE TABLE MyTable).executeUpdate();
}
});
To create TransactionTemplate just use injected PlatformTransactionManager
:
要创建 TransactionTemplate 只需使用注入PlatformTransactionManager
:
transactionTemplate = new TransactionTemplate(platformTransactionManager);
And if you want to use new transaction just invoke
如果您想使用新交易,只需调用
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
回答by membersound
As a workaround I now created a new EntityManager
explicit using the EMF
, and starting the transaction manually.
作为一种解决方法,我现在EntityManager
使用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();
}
That's probably not ideal, but works for the moment.
这可能并不理想,但目前有效。
回答by Oliver Drotbohm
Spring Data JPA automatically runs CRUD method in transactions for you (without needing to set up anything except a transaction manager). If you want to use transactions for your query methods, you can simply add @Transactional
to these:
Spring Data JPA 会自动为您在事务中运行 CRUD 方法(无需设置除事务管理器以外的任何内容)。如果你想在查询方法中使用事务,你可以简单地添加@Transactional
到这些:
interface MyRepository extends CrudRepository<MyEntity, Integer> {
@Transactional
@Modifying
@Query(value = "TRUNCATE TABLE MyTable", nativeQuery = true)
void clear();
}
On a more general note, what you have declared here is logically equivalent to CrudRepository.deleteAll()
, except that it (your declaration) doesn't honor JPA-level cascades. So I wondered that's really what you intended to do. If you're using Spring Boot, the activation and transaction manager setup should be taken care of for you.
更一般地说,您在此处声明的内容在逻辑上等同于CrudRepository.deleteAll()
,只是它(您的声明)不支持 JPA 级级联。所以我想知道这真的是你打算做的。如果您使用的是 Spring Boot,则应该为您处理激活和事务管理器设置。
If you want to use @Transactional
on the service level, you need to setup both a JpaTransactionManager
andactivate annotation based transaction management through either <tx:annotation-driven />
or @EnableTransactionManagement
(looks like the activation was the missing piece on your attempt to create transactions on the service layer).
如果您想@Transactional
在服务级别使用,则需要通过或(看起来激活是您尝试在服务层上创建事务时缺少的部分)来设置 aJpaTransactionManager
和activate 基于注释的事务管理。<tx:annotation-driven />
@EnableTransactionManagement
回答by Manish Bansal
@Transactional
annotation should not be applied on Dao method but on a service method. Although your code says DatabaseService
is a service, but by inserting EntityManger
inside a service does not make any sense.
@Transactional
注解不应应用于 Dao 方法,而应应用于服务方法。虽然你的代码说的DatabaseService
是一个服务,但是EntityManger
在服务里面插入是没有任何意义的。
Correct way to implement is to create a Dao like below.
正确的实现方法是创建一个如下所示的 Dao。
@Repository
public class DatabaseDao {
@PersistenceContext
private EntityManager em;
public void clear() {
em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate();
}
}
Then call the dao method from a service method with @Transactional
annotation.
然后从带有@Transactional
注解的服务方法中调用 dao 方法。
@Service
public class DatabaseService {
@Autowired
private DatabaseDao dao;
@Transactional
public void clear() {
dao.clear();
}
}
Also, add @EnableTransactionManagement
in your Configuration
class
另外,@EnableTransactionManagement
在您的Configuration
班级中添加