I'm using Spring framework.
The structure of my project is Controller ?? Service ?? Logic.
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.
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)
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
is initialized in main thread.
What should I do if I want to execute entityManager.flush()? Or is my understanding about the source wrong?
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
@Async("taskExecutor")
public CompletableFuture<InventoryInfoListDto> execute(final ItemRequestDto item) {
return CompletableFuture.completedFuture(logic.execute(item));
}
Logic.java
@Transactional(rollbackFor = Throwable.class, timeout = 60)
public ResponseDto execute(final ItemRequestDto item) {
// process...
}
I created an issue in github.
https://github.com/spring-projects/spring-framework/issues/23325
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…