簡體   English   中英

當我添加@Async 時,entityManager.flush() 將引發異常

[英]When I add @Async, entityManager.flush() will raise an exception

我正在使用 Spring 框架。

我的項目結構是Controller ➡️ Service ➡️ Logic

我在 Logic 類中添加了@Transactional 我正在使用 EntityManager 進行數據庫操作。 在每次數據庫操作(選擇、更新...)之后,我調用entityManager.flush()方法。 一切安好。

但是為了提高性能,我在Service類中添加了@Async 然后當我調用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)

調試了一下源碼,發現EntityManager是通過使用ThreadLocal綁定線程的,但是當我添加@Async的時候,會出現一個新線程,這個新線程會創建一個新的EntityManager,就OK了。 但是當我調用 entityManager.flush() 時,它會檢查 Transaction 並調用JdbcResourceLocalTransactionCoordinatorImpl.isJoined()方法, physicalTransactionDelegate為 null,因此會引發異常。

physicalTransactionDelegate在主線程中初始化。

如果要執行entityManager.flush()怎么辦? 或者我對來源的理解是錯誤的?

控制器.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));
        }
    }

服務.java

@Async("taskExecutor")
    public CompletableFuture<InventoryInfoListDto> execute(final ItemRequestDto item) {
        return CompletableFuture.completedFuture(logic.execute(item));
    }

邏輯.java

@Transactional(rollbackFor = Throwable.class, timeout = 60)
public ResponseDto execute(final ItemRequestDto item) {
    // process...
}

我在github中創建了一個問題。 https://github.com/spring-projects/spring-framework/issues/23325

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

我認為這是一個錯誤。

  • 沒有@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()進行檢查。
  • 使用@Async
    • 當請求到來時,將調用TransactionSynchronizationManager.bindResource((Object key, Object value) ,並將一個新的 EntityManager 綁定到當前線程。
    • 如果存在@Async,則會創建一個新線程。
    • 然后EntityManagerFactoryUtils.doGetTransactionalEntityManager(EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction)獲取綁定的EntityManager。 但是在這個新線程中,沒有綁定EntityManager,所以會創建一個新的EntityManager.joinTransaction()但不會調用EntityManager.joinTransaction() 所以TransactionDriverControlImpl physicalTransactionDelegate不會被初始化。
    • 當我們調用EntityManager.flush() 由於TransactionDriverControlImpl physicalTransactionDelegate為 null,將引發異常。

為什么在新線程中創建新 EntityManager 時不調用EntityManager.joinTransaction()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM