【问题标题】:Spring TransactionInterceptor with Java 8 CompletableFuture带有 Java 8 CompletableFuture 的 Spring TransactionInterceptor
【发布时间】:2017-11-18 03:10:22
【问题描述】:

我们在业务层方法上使用 Spring @Transactional 注解。当我们迁移到 Java 8 时,这些方法已被转换为使用 Java 8 的异步特性并使用可完成的 Futures 并将多个异步调用链接到数据层。例如。

@Transactional
public CompletableFuture<Entity> updateEntity(ID id) {
   repository1.get(id)
      .thenComposeAsync(item -> repository1.save(), executor); 
}

上面的代码演示了我们想要链接多个数据库层异步调用。

@Transactional注解此时似乎只支持阻塞调用,所有事务上下文信息都保存在ThreadLocal中。上述方法创建了 3 个交易。一旦返回未来,外部事务就会开始并完成。 repository1.get(id) 在另一个事务中运行,repository1.save() 在它自己的事务中运行。

是否有一种标准方法可以在单个事务中运行多个异步调用,而无需重写事务拦截器。似乎我们还需要将TransactionSynchronizationManager 中的线程局部变量从一个线程复制到另一个线程,以使其在单个事务中执行。
如果 Spring 团队计划增强 spring-tx 以支持此用例,请感谢他们的反馈。

【问题讨论】:

  • 好问题,但众所周知,spring transactional 绑定到单个线程 - 我非常怀疑是否有可能做你想做的事

标签: spring java-8 spring-data spring-transactions completable-future


【解决方案1】:

我在problem I was trying to solve 中遇到了类似的障碍。

简而言之:

  1. 最简单和最多的effective way to solve this 是通过生产者-消费者模式(并通过管道连接到单个事务性工作线程)。请参见下面的示例。
  2. 如果你的问题是温和的,你可以使用batching
  3. 您可以使用 JTA 自然地解决此问题,其中事务管理器根据定义处理此问题(正如您所问的 spring-tx has supported since 2003),这可能会给您带来比您预想的更多的开销(和复杂性)
  4. 摆弄事务拦截器和TransactionSynchronizationManager 并通过大多数 TransactionManager 无法应对的 hack-ey 解决方法来解决

我在单线程中的事务对象大致如下:

@Service
public class TransactionalObject {

    @Autowired
    private BlockingQueue<Runnable> queue;

    @Transactional
    public void consumeTransactionalRunnables() {
         try {
                while (!Thread.currentThread().isInterrupted()) {
                    Runnable execution = queue.take();
                    if (execution instanceof StopExecution) {
                        return;
                    }
                    execution.run();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }               
    }

}

这个对象在它自己的线程上,生产者线程putBlockingQueue queue。此代码示例中的poison pillStopExecution Runnable 类型。

我发现此解决方案的性能开销最小(与其他选项相比)、易于管理、易于阅读和微创。

【讨论】:

  • 这是否意味着我的应用程序中的所有数据库调用都将在单个线程上执行?如果是这样,就会存在可扩展性问题,不是吗?
  • 关于选项 4,是否有一个 TransactionManager 实现通过在 Spring 的线程(自定义执行器)之间传递轻量级上下文来工作?
  • 如果你沿着这条路走,你很可能有多个事务消费者(取决于你想要多少事务)来执行每个事务,并且可以轻松地创建它们的工厂。更好的是,您创建自定义 Executor 的想法与 CompletableFuture 配合得很好。您可以在 execute/submit 方法中使用 Spring TransactionTemplate 并使用 TM 实例化它。
猜你喜欢
  • 1970-01-01
  • 2015-04-19
  • 2017-05-14
  • 2016-03-11
  • 1970-01-01
  • 2014-05-23
  • 2015-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多