简体   繁体   English

将反应式(r2dbc)批处理与事务一起使用

[英]Using reactive(r2dbc) batch with transaction

I am trying to use create a batch using reactive r2dbc and use transactional to annotate the method.我正在尝试使用反应式 r2dbc 创建一个批处理并使用事务性来注释该方法。 But it looks like that if I use both @Transactional and batch code the code just hangs and doesn't work.但看起来如果我同时使用@Transactional和批处理代码,代码就会挂起并且不起作用。

Below is the code:下面是代码:

@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;
}

You are breaking the reactive chain.你正在打破反应链。

In reactive programming nothing happens until you subscribe .在响应式编程中,除非您订阅,否则不会发生任何事情

So what does that mean, well I can show it in a little example.那是什么意思,我可以用一个小例子来展示它。

// If running this, nothing happens
Mono.just("Foobar");

while:尽管:

Mono.just("Foobar").subscribe(s -> System.out.println(s));

Will print:将打印:

Foobar

This also applies if you have a function如果您有 function,这也适用

public void getString() {
    Mono.just("Foobar");
}

// Nothing happens, you have declared something 
// but it will never get run, no one is subscribing
getString();

What you need to do:你需要做什么:

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));

So what is happening?那么发生了什么? Well, in reactive programming as soon as someone subscribes to a Mono or a Flux, the program will traverse up and build a chain of callbacks until it finds the producer that starts producing values (in my case the just statement).好吧,在响应式编程中,只要有人订阅了 Mono 或 Flux,程序就会向上遍历并构建一个回调链,直到找到开始产生值的生产者(在我的例子中是 just 语句)。 This phase is called the "assembly phase".这个阶段称为“组装阶段”。 When this phase is done, the reactive chain will start producing values to whomever that is subscribing.当这个阶段完成后,反应链将开始为订阅者产生价值。

If no one is subscribing, no chain will be built.如果没有人订阅,就不会建立链。

So who is the subscriber?那么订户是谁呢? It is usually the end consumer of the value.它通常是价值的最终消费者。 So for instance, the webpage that initiated the call, or a mobile app, but could also be your Spring Boot service if it's the one that initiates the call (in for instance a cron job).因此,例如,发起呼叫的网页或移动应用程序,但也可能是您的 Spring 启动服务,如果它是启动呼叫的服务(例如 cron 作业)。

So lets look at your code:所以让我们看看你的代码:

@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;
}

So how do you solve it?那你怎么解决呢?

Well you need to not break the reactive chain.那么你不需要打破反应链。 This is basic reactive programming.这是基本的反应式编程。

@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.       
}

"I don't want to return anything" “我什么都不想退货”

Well that's what the Mono#then statement is for.这就是Mono#then语句的用途。 You see when each part in the chain is completed it will signal its completion and then pass the values from one part to the next, and then signal again, and pass the value etc. When we reach the then statement it will just signal COMPLETE and not return anything (or actually it is returning a Mono<Void> because null is not allowed in reactive chains).您会看到,当链中的每个部分完成时,它将发出完成信号,然后将值从一个部分传递到下一个部分,然后再次发出信号,并传递值等。当我们到达then语句时,它只会发出COMPLETE和不返回任何东西(或者实际上它返回Mono<Void>因为 null 在反应链中是不允许的)。 You must always return, so that each step can pass on its COMPLETE signal.您必须始终返回,以便每一步都可以传递其COMPLETE信号。

Also I removed the StepVerifier you are using in your code, because it is usually used to verify steps in unit tests, and not used in production code.我还删除了您在代码中使用的 StepVerifier,因为它通常用于验证单元测试中的步骤,而不是在生产代码中使用。 You can read more about it here StepVerifier .你可以在这里阅读更多关于它的信息StepVerifier

If you want to learn reactive programming, which I suggest you do because it is amazing and I love it, I recommend strongly that you read the excellent reactor documentation Introduction to reactive programming where they will explain the concepts of nothing happens until you subscribe etc.如果您想学习响应式编程,我建议您这样做,因为它很棒而且我喜欢它,我强烈建议您阅读优秀的反应器文档介绍响应式编程,其中将解释在您订阅之前什么都不会发生的概念等。

Your problem is:你的问题是:

return null;

You should return a Mono/Flux in a reactive application, even there is no items in the flow, return a Mono.emtpy instead.您应该在反应式应用程序中返回 Mono/Flux,即使流程中没有项目,也应该返回Mono.emtpy

Check my example insert Multi records .检查我的示例插入多条记录

And the tests, use StepVerify to verify the result .和测试,使用StepVerify 来验证结果

For transaction support in WebFlux applications, you have to read the relative docs to check if it is supported well as a general local transaction or the limitation when using it.对于 WebFlux 应用程序中的事务支持,您必须阅读相关文档以检查它是否被支持作为一般本地事务或使用它时的限制。

There are two approaches to use the transaction if it is supported well.如果支持良好,则有两种使用事务的方法。

  1. Inject a TransactionalOperator (similar to the traditional TransactionTemplate ) to wrap your business logic.注入一个TransactionalOperator (类似于传统的TransactionTemplate )来包装您的业务逻辑。
  2. Apply @Transaction annotation on classes or methods as general.一般在类或方法上应用@Transaction注释。

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

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