Java Spring集成在下一次集成之前完成异步转换

Java Spring集成在下一次集成之前完成异步转换,java,jpa,asynchronous,spring-integration,spring-integration-dsl,Java,Jpa,Asynchronous,Spring Integration,Spring Integration Dsl,我有一个集成流程,它定期轮询数据库,以检索任何尚未处理的MachineLine实体并对其进行处理。流检索MachineLine对象的集合,然后我希望将其拆分为单个对象,将这些对象转换为ReportDetails对象,并将转换后的对象持久化到数据库中的另一个表中。流量定义如下: @Bean public IntegrationFlow processMachineLine() { return IntegrationFlows .from(Jpa.inboundAd

我有一个集成流程,它定期轮询数据库,以检索任何尚未处理的
MachineLine
实体并对其进行处理。流检索
MachineLine
对象的集合,然后我希望将其拆分为单个对象,将这些对象转换为
ReportDetails
对象,并将转换后的对象持久化到数据库中的另一个表中。流量定义如下:

@Bean
public IntegrationFlow processMachineLine() {
    return IntegrationFlows
            .from(Jpa.inboundAdapter(this.entityManager)
                            .entityClass(MachineLine.class)
                            .jpaQuery(this.machineService.retrieveUnprocessedLinesQuery()),
                    e -> e.poller(Pollers.fixedDelay(5000)))
            .split()
            .transform(MachineLine.class, this::transformMachineLineToReportDetails)
            .handle(Jpa.outboundAdapter(this.entityManager)
                            .entityClass(ReportDetails.class),
                    ConsumerEndpointSpec::transactional)
            .get();
}
上面的定义很好,但速度很慢。
transformMachineNetoReportDetails
方法向另一个服务发送HTTP请求,该请求需要几秒钟才能响应。对于当前流定义,每个
MachineLine
对象在对其执行相同操作之前,都会等待前一个对象被转换和持久化

因此,理想的解决方案是异步执行此转换和持久性。一种可能的解决方案是在
.split()
.transform(…)
之间插入以下行:

.channel(新执行器通道(Executors.newCachedThreadPool())

但是,这允许JPA入站适配器在处理和持久化最后一次轮询的结果之前再次轮询数据库。这意味着前一次数据库轮询返回的、在下一次轮询之前未被转换和持久化的任何
MachineLine
实体将被再次检索,并尝试进行第二次转换和持久化。这显然会导致不必要的资源开销,并且在多个具有相同ID的
ReportDetails
对象试图持久化到数据库时也会产生错误


是否有一种方法可以异步转换
MachineLine
对象,但确保在上一次轮询的结果通过流完成之前不会再次轮询数据库(即,所有
MachineLine
对象都被转换并持久化)?

我通过自定义
AbstractMessageSourceAdvice
查看它的唯一方法是针对一些
原子布尔
标志(也可以是bean)签入
beforeceive()
。由于您使用的是
轮询器.fixedDelay(5000)
,因此您的轮询策略仍然是单线程的。因此,当
AbstractMessageSourceAdvice
不允许对JPA执行轮询时,我们最好不要让同一线程对JPA执行轮询。在begging中,布尔标志应该是
true
,并且在提到的
split()
之前将其更改为
false
。您可以将
publishSubscribeChannel()
作为两个订阅者使用。甚至在
AbstractMessageSourceAdvice
实现中也可以这样做,有点像
compareAndSet(true,false)
中的
beforeReceive()
实现

然后在转换后使用
ExecutorChannel
进行拆分和持久化

在流的末尾,您需要放置一个带有两个订阅者的
publishSubscribeChannel()
——1<代码>句柄(Jpa.outboundAdapter(this.entityManager);2.
aggregate()
等待所有拆分的项目完成。然后
aggregate()
放置一个简单的
句柄(m->pollingFlagBean().set(true))

仅此而已:只有当所有项目都被处理并聚合回组时,才会发生新的轮询。只有在这之后,您才允许轮询器使用该
AtomicBoolean
再次进行轮询

您还可以考虑将此标志逻辑与<>代码> SimeActive IdMeLeSaveSuthCeope更改活动模式和被动模式之间的轮询周期,以避免在等待聚合时大空闲。


任何其他异步解决方案仍然无法为您工作,因为切换到其他线程将立即释放轮询进程,让它再次旋转。

感谢您的回复,Artem!这是一个非常有用的答案。此外,感谢您和团队其他成员在Spring集成方面所做的出色工作。项目太棒了!