[英]Using reactive(r2dbc) batch with transaction
我正在尝试使用反应式 r2dbc 创建一个批处理并使用事务性来注释该方法。 但看起来如果我同时使用@Transactional
和批处理代码,代码就会挂起并且不起作用。
下面是代码:
@Transactional
@GetMapping("/batchFetchData")
public Flux<Object> batchFetch() {
long startTime = System.currentTimeMillis();
Mono.from(databaseConfiguration.connectionFactory().create())
.flatMapMany(connection -> Flux.from(connection
.createBatch() /* **Creating batch***/
.add("SELECT * FROM xtable where xId = 232323")
.add("SELECT * FROM ytable where yId = 454545")
.add("SELECT * FROM ztable where zId = 676767")
//.execute())); /* **Execution batch***/
.execute())).as(StepVerifier::create)
.expectNextCount(3) /* **Expect count batch***/
.verifyComplete(); /* **Verify batch***/
LOGGER.info("Time taken to batchFetch "+(System.currentTimeMillis() - startTime));
return null;
}
你正在打破反应链。
在响应式编程中,除非您订阅,否则不会发生任何事情。
那是什么意思,我可以用一个小例子来展示它。
// If running this, nothing happens
Mono.just("Foobar");
尽管:
Mono.just("Foobar").subscribe(s -> System.out.println(s));
将打印:
Foobar
如果您有 function,这也适用
public void getString() {
Mono.just("Foobar");
}
// Nothing happens, you have declared something
// but it will never get run, no one is subscribing
getString();
你需要做什么:
public Mono<String> getString() {
// This could be saving to a database or anything, this will now get run
return Mono.just("Now this code will get run");
}
// The above got run, we can prove it by printing
getString().subscribe(s -> System.out.println(s));
那么发生了什么? 好吧,在响应式编程中,只要有人订阅了 Mono 或 Flux,程序就会向上遍历并构建一个回调链,直到找到开始产生值的生产者(在我的例子中是 just 语句)。 这个阶段称为“组装阶段”。 当这个阶段完成后,反应链将开始为订阅者产生价值。
如果没有人订阅,就不会建立链。
那么订户是谁呢? 它通常是价值的最终消费者。 因此,例如,发起呼叫的网页或移动应用程序,但也可能是您的 Spring 启动服务,如果它是启动呼叫的服务(例如 cron 作业)。
所以让我们看看你的代码:
@Transactional /* **Transaction** */
@GetMapping("/batchFetchData")
public Flux<Object> batchFetch() {
long startTime = System.currentTimeMillis();
// Here you declare a Mono but ignoring the return type so breaking the reactive chain
Mono.from(databaseConfiguration.connectionFactory().create())
.flatMapMany(connection -> Flux.from(connection
.createBatch() /* **Creating batch***/
.add("SELECT * FROM xtable where xId = 232323")
.add("SELECT * FROM ytable where yId = 454545")
.add("SELECT * FROM ztable where zId = 676767")
//.execute())); /* **Execution batch***/
.execute())).as(StepVerifier::create)
.expectNextCount(3) /* **Expect count batch***/
.verifyComplete(); /* **Verify batch***/
// Here at the end you have no subscriber
LOGGER.info("Time taken to batchFetch "+(System.currentTimeMillis() - startTime));
// Null is not allowed in reactive chains
return null;
}
那你怎么解决呢?
那么你不需要打破反应链。 这是基本的反应式编程。
@Transactional
@GetMapping("/batchFetchData")
public Mono<Void> batchFetch() {
long startTime = System.currentTimeMillis();
// we return here so that the calling client
// can subscribe and start the chain
return Mono.from(databaseConfiguration.connectionFactory().create())
.flatMapMany(connection -> Flux.from(connection
.createBatch()
.add("SELECT * FROM xtable where xId = 232323")
.add("SELECT * FROM ytable where yId = 454545")
.add("SELECT * FROM ztable where zId = 676767")
.execute()))
.then();
// then() statement throws away whatever the return
// value is and just signals to the calling client
// when everything is done.
}
“我什么都不想退货”
这就是Mono#then
语句的用途。 您会看到,当链中的每个部分完成时,它将发出完成信号,然后将值从一个部分传递到下一个部分,然后再次发出信号,并传递值等。当我们到达then
语句时,它只会发出COMPLETE和不返回任何东西(或者实际上它返回Mono<Void>
因为 null 在反应链中是不允许的)。 您必须始终返回,以便每一步都可以传递其COMPLETE信号。
我还删除了您在代码中使用的 StepVerifier,因为它通常用于验证单元测试中的步骤,而不是在生产代码中使用。 你可以在这里阅读更多关于它的信息StepVerifier 。
如果您想学习响应式编程,我建议您这样做,因为它很棒而且我喜欢它,我强烈建议您阅读优秀的反应器文档介绍响应式编程,其中将解释在您订阅之前什么都不会发生的概念等。
你的问题是:
return null;
您应该在反应式应用程序中返回 Mono/Flux,即使流程中没有项目,也应该返回Mono.emtpy
。
检查我的示例插入多条记录。
和测试,使用StepVerify 来验证结果。
对于 WebFlux 应用程序中的事务支持,您必须阅读相关文档以检查它是否被支持作为一般本地事务或使用它时的限制。
如果支持良好,则有两种使用事务的方法。
TransactionalOperator
(类似于传统的TransactionTemplate
)来包装您的业务逻辑。@Transaction
注释。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.