当我添加 @Async 时,entityManager.flush() 将引发异常

hua*_*hui 5 java spring hibernate transactions

我正在使用 Spring 框架。

\n\n

我的项目的结构是Controller \xe2\x9e\xa1\xef\xb8\x8f Service \xe2\x9e\xa1\xef\xb8\x8f Logic

\n\n

@Transactional在逻辑课中添加了。我正在使用 EntityManager 进行数据库操作。每次数据库操作(选择、更新...)之后,我都会调用entityManager.flush()方法。\n一切都很好。

\n\n

但为了提高性能,我@Async在Service类中添加了。\n然后当我调用时会引发异常entityManager.flush()

\n\n
javax.persistence.TransactionRequiredException: no transaction is in progress\n    at org.hibernate.internal.SessionImpl.checkTransactionNeeded(SessionImpl.java:3505)\n    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1427)\n    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1423)\n    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n    at java.lang.reflect.Method.invoke(Method.java:498)\n    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350)\n    at com.sun.proxy.$Proxy150.flush(Unknown Source)\n    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n    at java.lang.reflect.Method.invoke(Method.java:498)\n    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:305)\n    at com.sun.proxy.$Proxy150.flush(Unknown Source)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我调试了源码,发现EntityManager是通过ThreadLocal绑定到线程的,但是当我添加@Async时,就会出现一个新的线程,这个新的线程会创建一个新的EntityManager,就可以了。但是当我调用entityManager.flush()时,它会检查Transaction和调用JdbcResourceLocalTransactionCoordinatorImpl.isJoined()方法physicalTransactionDelegate是否为null,因此会引发异常。

\n\n

physicalTransactionDelegate在主线程中初始化。

\n\n

如果我想执行entityManager.flush()该怎么办?还是我对来源的理解有误?

\n\n

控制器.java

\n\n
@GetMapping(value = "/getTest", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)\n    public ResponseDto execute(@Valid final RequestDto requestDto) {\n        List<CompletableFuture<Dto>> completableFutures = new ArrayList<>();\n        for (ItemRequestDto item : requestDto.getItemRequestDtoList()) {\n            completableFutures.add(service.execute(item));\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

服务.java

\n\n
@Async("taskExecutor")\n    public CompletableFuture<InventoryInfoListDto> execute(final ItemRequestDto item) {\n        return CompletableFuture.completedFuture(logic.execute(item));\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

逻辑.java

\n\n
@Transactional(rollbackFor = Throwable.class, timeout = 60)\npublic ResponseDto execute(final ItemRequestDto item) {\n    // process...\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我在 github 中创建了一个问题。\n https://github.com/spring-projects/spring-framework/issues/23325

\n

hua*_*hui 1

我找到了解决方案。只需添加entityManager.joinTransaction();。