简体   繁体   English

CompletableFuture 与 Spring 事务

[英]CompletableFuture vs Spring Transactions

Idea主意

I have a processing method which takes in a list of items and processes them asynchronously using external web service.我有一个处理方法,它接收项目列表并使用外部 Web 服务异步处理它们。 The process steps also persist data while processing.处理步骤还在处理时保留数据。 At the end of whole process, I want to persist the whole process along with each processed results as well.在整个过程结束时,我想将整个过程与每个处理的结果一起持久化。

Problem问题

I convert each item in the list into CompletableFuture and run a processing task on them, and put them back into an array of futures.我将列表中的每个项目转换为CompletableFuture并对它们运行处理任务,然后将它们放回期货数组中。 Now using its .ofAll method (in sequence method) to complete future when all the submitted tasks are completed and return another CompletableFuture which holds the result.现在使用它的.ofAll方法(按顺序方法)在所有提交的任务完成后完成 future 并返回另一个保存结果的CompletableFuture

When I want to get that result, I call .whenComplete(..) , and would want to set the returned result into my entity as data, and then persist to the database, however the repository save call just does nothing and continues threads just continue running, it's not going past the repository save call.当我想得到那个结果时,我调用.whenComplete(..) ,并希望将返回的结果作为数据设置到我的实体中,然后保存到数据库中,但是存储库保存调用什么都不做,只是继续线程继续运行,它不会通过存储库保存调用。

 @Transactional
 public void process(List<Item> items) {
   List<Item> savedItems = itemRepository.save(items);

   final Process process = createNewProcess();

   final List<CompletableFuture<ProcessData>> futures = savedItems.stream()
     .map(item -> CompletableFuture.supplyAsync(() -> doProcess(item, process), executor))
     .collect(Collectors.toList());

   sequence(futures).whenComplete((data, throwable) -> {
     process.setData(data);
     processRepository.save(process); // <-- transaction lost?
     log.debug("Process DONE"); // <-- never reached
   });
  }

Sequence method序列法

 private static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futures) {
    CompletableFuture<Void> allDoneFuture =
      CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
    return allDoneFuture.thenApply(v ->
      futures.stream().map(CompletableFuture::join).collect(Collectors.toList())
    );
  }

What is happening?怎么了? Why is the persist call not passing.为什么持久调用没有通过。 Is the thread that started the transaction not able to commit the transaction or where does it get lost?启动事务的线程是否无法提交事务或它在哪里丢失? All the processed data returns fine and is all good.所有处理过的数据都返回正常并且一切正常。 I've tried different transaction strategies, but how is it possible to control which thread is gonna finish the transaction, if it's the case?我尝试了不同的事务策略,但如果是这样,如何控制哪个线程将完成事务?

Any advice?有什么建议吗?

The reason of your problem is, as said above, that the transaction ends when the return of method process(..) is reached.您的问题的原因是,如上所述,当达到方法 process(..) 的返回时,事务结束。

What you can do, is create the transaction manually, that gives you full control over when it starts and ends.您可以做的是手动创建事务,这样您就可以完全控制它的开始和结束时间。

Remove @Transactional删除@Transactional

Autowire the TransactionManager then in process(..) :自动装配 TransactionManager 然后在 process(..) :

    TransactionDefinition txDef = new DefaultTransactionDefinition();
    TransactionStatus txStatus = transactionManager.getTransaction(txDef);
    try {
    //do your stuff here like
        doWhateverAsync().then(transactionManager.commit(txStatus);)
    } catch (Exception e) {
        transactionManager.rollback(txStatus);
        throw e;
    }

In case of Spring Boot Application , you need following configurations.在 Spring Boot Application 的情况下,您需要以下配置。

The main application method should be annotated with @EnableAsync.主应用程序方法应使用@EnableAsync 进行注释。

@Async annotation should be on the top of method having @Transactional annotation. @Async 注释应该位于具有 @Transactional 注释的方法的顶部。 This is necessary to indicate processing will be taking place in child thread.这对于指示将在子线程中进行处理是必要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM