当我添加@Async时,entityManager.flush()将引发异常 [英] When I add @Async, entityManager.flush() will raise an exception
问题描述
我正在使用Spring框架.
I'm using Spring framework.
我的项目的结构为 Controller➡️Service➡️Logic .
我在逻辑类中添加了@Transactional
.我正在使用EntityManager进行数据库操作.在每个数据库操作(选择,更新...)之后,我正在调用entityManager.flush()
方法.
一切都很好.
I added @Transactional
in Logic class. I am using EntityManager to do DB operations. After every db operation(select, update...), I am calling entityManager.flush()
method.
Everything is fine.
但是为了提高性能,我在Service类中添加了@Async
.
然后,当我调用entityManager.flush()
时会引发异常.
But in order to improve performance, I added @Async
in Service class.
Then an exception is raised when I am calling entityManager.flush()
.
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.SessionImpl.checkTransactionNeeded(SessionImpl.java:3505)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1427)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1423)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350)
at com.sun.proxy.$Proxy150.flush(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:305)
at com.sun.proxy.$Proxy150.flush(Unknown Source)
我调试了源代码,发现使用ThreadLocal将EntityManager绑定到线程,但是当我添加@Async时,将有一个新线程,该新线程将创建一个新的EntityManager,这是可以的.但是,当我调用entityManager.flush()时,它将检查Transaction并调用JdbcResourceLocalTransactionCoordinatorImpl.isJoined()
方法,physicalTransactionDelegate
为空,因此将引发异常.
I debugged the source and found that EntityManager is bound to thread by using ThreadLocal, but when I added @Async, there will be a new thread, this new thread will create a new EntityManager, that is OK. But when I call entityManager.flush(), it will check Transaction and call JdbcResourceLocalTransactionCoordinatorImpl.isJoined()
method,physicalTransactionDelegate
is null, so the exception will be raised.
physicalTransactionDelegate
在主线程中初始化.
physicalTransactionDelegate
is initialized in main thread.
如果我想执行entityManager.flush(),该怎么办?还是我对来源的理解不正确?
What should I do if I want to execute entityManager.flush()? Or is my understanding about the source wrong?
Controller.java
Controller.java
@GetMapping(value = "/getTest", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseDto execute(@Valid final RequestDto requestDto) {
List<CompletableFuture<Dto>> completableFutures = new ArrayList<>();
for (ItemRequestDto item : requestDto.getItemRequestDtoList()) {
completableFutures.add(service.execute(item));
}
}
Service.java
Service.java
@Async("taskExecutor")
public CompletableFuture<InventoryInfoListDto> execute(final ItemRequestDto item) {
return CompletableFuture.completedFuture(logic.execute(item));
}
Logic.java
Logic.java
@Transactional(rollbackFor = Throwable.class, timeout = 60)
public ResponseDto execute(final ItemRequestDto item) {
// process...
}
我在github中创建了一个问题. https://github.com/spring-projects/spring-framework/issues/23325
推荐答案
我认为这是一个错误.
- 没有@Async
- 当请求到来时,将调用
TransactionSynchronizationManager.bindResource((Object key, Object value)
,并将新的EntityManager绑定到当前线程. - 然后,调用
EntityManagerFactoryUtils.doGetTransactionalEntityManager(EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction)
以获取绑定的EntityManager.在此方法内部,它将调用EntityManager.joinTransaction()
.此方法将调用JdbcResourceLocalTransactionCoordinatorImpl.getTransactionDriverControl()
初始化TransactionDriverControlImpl physicalTransactionDelegate
. - 当我们致电
EntityManager.flush()
时.JdbcResourceLocalTransactionCoordinatorImpl.isJoined()
将被调用进行检查.
- With No @Async
- When request comes,
TransactionSynchronizationManager.bindResource((Object key, Object value)
will be called, and a new EntityManager will be bound to current thread. - Then,
EntityManagerFactoryUtils.doGetTransactionalEntityManager(EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction)
is called to get the bound EntityManager. Inside this method, it will callEntityManager.joinTransaction()
. This method will callJdbcResourceLocalTransactionCoordinatorImpl.getTransactionDriverControl()
to initializeTransactionDriverControlImpl physicalTransactionDelegate
. - When we call
EntityManager.flush()
.JdbcResourceLocalTransactionCoordinatorImpl.isJoined()
will be called to check.
- 当请求到来时,将调用
TransactionSynchronizationManager.bindResource((Object key, Object value)
,并将新的EntityManager绑定到当前线程. - 如果存在@Async,将创建一个新线程.
- 然后,调用
EntityManagerFactoryUtils.doGetTransactionalEntityManager(EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction)
以获取绑定的EntityManager.但是在这个新线程中,没有绑定的EntityManager,因此将创建一个新的,但不会调用EntityManager.joinTransaction()
.因此TransactionDriverControlImpl physicalTransactionDelegate
将不会初始化. - 当我们致电
EntityManager.flush()
时.由于TransactionDriverControlImpl physicalTransactionDelegate
为null,将引发异常.
- When request comes,
TransactionSynchronizationManager.bindResource((Object key, Object value)
will be called, and a new EntityManager will be bound to current thread. - If there is a @Async, a new thread will be created.
- Then,
EntityManagerFactoryUtils.doGetTransactionalEntityManager(EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction)
is called to get the bound EntityManager. But in this new thread, there is no bound EntityManager, so a new one will be created, butEntityManager.joinTransaction()
will not be called. SoTransactionDriverControlImpl physicalTransactionDelegate
will not be initialized. - When we call
EntityManager.flush()
. SinceTransactionDriverControlImpl physicalTransactionDelegate
is null, an exception will be raised.
为什么在新线程中创建新的EntityManager时不调用
EntityManager.joinTransaction()
?Why
EntityManager.joinTransaction()
is not called when a new EntityManager is created in a new thread?这篇关于当我添加@Async时,entityManager.flush()将引发异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- When request comes,
- 当请求到来时,将调用