简体   繁体   English

如何将 if-else 语句包含到反应流中

[英]How to include if-else statements into reactive flow

I have a Spring Webflux reactive service which receives a DTO and inserts it into multiple table.我有一个 Spring Webflux 反应式服务,它接收 DTO 并将其插入到多个表中。 Sometimes we may need to skip inserting into some tables based on the incoming DTO.有时我们可能需要根据传入的 DTO 跳过插入某些表。

These are the requirements:这些是要求:

  1. Create a new Client创建一个新客户端
  2. Create a new Client referral if the client referral is present in the DTO.如果 DTO 中存在客户端引荐,则创建新的客户端引荐。
  3. Create a client Secondary contact if present in the DTO如果存在于 DTO 中,则创建客户端次要联系人
  4. Create client phones if present in the DTO创建客户端电话(如果存在于 DTO 中)

Questions:-问题:-

  1. Not sure how to apply the if condition in the reactive flow.不确定如何在反应流中应用 if 条件。
  2. Is there any better way of doing this?有没有更好的方法来做到这一点?
  3. Here, except the first one all other operation can run in parallel.在这里,除了第一个之外,所有其他操作都可以并行运行。
public Mono<ServerResponse> createClientProfile(ServerRequest request) {
        return secContext.retrieveUser().flatMap(usr -> {
            return request.bodyToMono(ClientDto.class).flatMap(client -> {
                return toNewClient(client, usr).flatMap(clientRepository::save).flatMap(clientRes -> {
                    return toNewClientReferral(clientRes.getClientId(), client.getDiscount(), usr)
                            .flatMap(clientReferralRepository::save).flatMap(clientReferralRes -> {
                                return toNewClientSyContact(clientRes.getClientId(), client.getSecondary(), usr)
                                        .flatMap(clientSyContactRepository::save).flatMap(clientSyContactRes -> {
                                            return clientPhoneRepository
                                                    .saveAll(toNewClientPhone(clientRes.getClientId(), client.getPhones(), usr))
                                                    .collectList().flatMap(phoneRes -> {
                                                        return ServerResponse
                                                                .created(URI.create(String.format(CLIENT_URI_FORMAT,
                                                                        clientRes.getClientId())))
                                                                .contentType(APPLICATION_JSON).build();
                                                    });
                                        });
                            });
                });
            });
        });

    }

private Mono<Referral> toNewClientReferral(final long clientId, final Discount dto) {
        Referral referral = Referral.of(clientId, 
                dto.getName(), dto.getType(), dto.getAmount(), dto.getStatus());

        return Mono.just(referral);
    }

client.getDiscount() can be null, client.getDiscount() 可以是 null,
client.getSecondary() can be null, client.getPhones() can be empty. client.getSecondary() 可以是 null,client.getPhones() 可以为空。

I separated the flow with 3 different methods.我用 3 种不同的方法将流程分开。

public void createSyContact(ServerRequest request, long clientId) {
        secContext.retrieveUser().flatMap(usr -> {
            return request.bodyToMono(ClientDto.class).flatMap(client -> {
                if (client.getSecondary() != null) {
                    return toNewClientSyContact(clientId, client.getSecondary(), usr)
                            .flatMap(clientSyContactRepository::save).flatMap(clientRes -> {
                                return Mono.just(clientRes.getClientId());
                            });
                } else {
                    return Mono.empty();
                }
            });
        });
    }

    public void createReferral(ServerRequest request, long clientId) {
        secContext.retrieveUser().flatMap(usr -> {
            return request.bodyToMono(ClientDto.class).flatMap(client -> {
                if (client.getDiscount() != null) {
                    return toNewClientReferral(clientId, client.getDiscount(), usr)
                            .flatMap(clientReferralRepository::save).flatMap(clientRes -> {
                                return Mono.just(clientRes.getClientId());
                            });
                } else {
                    return Mono.empty();
                }
            });
        });
    }

    public Mono<Long> createClientWithPhones(ServerRequest request) {
        return secContext.retrieveUser().flatMap(usr -> {
            return request.bodyToMono(ClientDto.class).flatMap(client -> {
                return toNewClient(client, usr).flatMap(clientRepository::save).flatMap(clientRes -> {
                    return clientPhoneRepository
                            .saveAll(toNewClientPhone(clientRes.getClientId(), client.getPhones(), usr)).collectList()
                            .flatMap(phoneRes -> {
                                return Mono.just(clientRes.getClientId());
                            });
                });
            });
        });
    }

Here, createClientWithPhones is mandatory, so no if check there.在这里, createClientWithPhones 是强制性的,所以如果检查那里没有。 But the other 2 methods createReferral & createSyContact have if checks.但是其他 2 种方法 createReferral 和 createSyContact 有 if 检查。 Need to execute createClientWithPhones first and it will return clientId.需要先执行createClientWithPhones,它会返回clientId。 This clientId should be used in createReferral & createSyContact.此 clientId 应在 createReferral 和 createSyContact 中使用。

public Mono<ServerResponse> createClientProfile(ServerRequest request) {
        final List<Long> clinetIdList = new ArrayList<>();
        createClientWithPhones(request).subscribe(result -> {
            clinetIdList.add(result.longValue());
            createSyContact(request, result.longValue());
            createReferral(request, result.longValue());
        });
        return ServerResponse
                .created(URI.create(String.format(CLIENT_URI_FORMAT,
                        clinetIdList.get(0))))
                .contentType(APPLICATION_JSON).build();
        
    }

Is this the way to handle this?这是处理这个问题的方法吗?

Well, I don't think there is a good understanding in general of the reactive library.好吧,我认为一般来说对反应库没有很好的理解。 What I mean is that generally people approach like Java 8 streams in that they are trying to do functional programming.我的意思是,通常人们会接近 Java 8 个流,因为他们正在尝试进行函数式编程。 Of course the reactive library is based in functional programming, but I think the purpose is to be asynchronous around blocking I/O.当然,响应式库是基于函数式编程的,但我认为其目的是围绕阻塞 I/O 进行异步处理。 Consider the (current) front page of the WebFlux project.考虑 WebFlux 项目的(当前)首页。

What is reactive processing?什么是反应式处理? Reactive processing is a paradigm that enables developers build non-blocking, asynchronous applications that can handle back-pressure (flow control).反应式处理是一种范式,使开发人员能够构建可以处理背压(流量控制)的非阻塞、异步应用程序。

So, this is a longwinded way of saying I think it is better to focus on where the I/O is happening rather than creating functional code.因此,这是一种冗长的说法,我认为最好关注 I/O 发生的位置,而不是创建功能代码。 If you need if statements, then you need if statements.如果你需要if语句,那么你需要if语句。 Instead of trying to figure out how to do if statements with functional programming try to figure out where the I/O is taking place and handle it in an asynchronous fashion.与其试图找出如何使用函数式编程的if语句,不如尝试找出 I/O 发生的位置并以异步方式处理它。 One "trick" I like to use is Mono::zip or Flux::zip .我喜欢使用的一个“技巧”是Mono::zipFlux::zip These functions combine many I/O calls into one publisher to be returned to the client.这些函数将许多 I/O 调用组合到一个发布者中以返回给客户端。 So, consider this example code.因此,请考虑此示例代码。

Let's make some reactive r2dbc functions:让我们创建一些响应式 r2dbc 函数:

Mono<Client> save(Client client) {
    client.id = 1L;
    System.out.println("Save client: " + client.id);
    return Mono.just(client);
}
Mono<Phone> save(Phone phone) {
    System.out.println("Save phone: " + phone.clientId);
    return Mono.just(phone);
}
Mono<Referral> save(Referral referral) {
    System.out.println("Save referral: " + referral.clientId);
    return Mono.just(referral);
}
Mono<Contact> save(Contact contact) {
    System.out.println("Save contact: " + contact.clientId);
    return Mono.just(contact);
}

We need some example classes to use:我们需要一些示例类来使用:

class DTO {
    Client client;
    List<Phone> phones;
    Optional<Contact> contact;
    Optional<Referral> referral;
}

class Client {
    Long id;
}

class Contact {
    Long clientId;
}

class Referral {
    Long clientId;
}

class Phone {
    Long clientId;
}

Our input is probably a Mono<DTO> since that is what the Request should supply, so our Service layer needs to start with that and return a Mono<Long> of the client id.我们的输入可能是Mono<DTO> ,因为这是 Request 应该提供的,所以我们的Service层需要从它开始并返回客户端 ID 的Mono<Long>

Mono<Long> doWork(Mono<DTO> monoDto) {
    return monoDto.flatMap(dto->{
        return save(dto.client).flatMap(client->{
            List<Mono<?>> publishers = new ArrayList<>();
            dto.phones.forEach(phone->{
                phone.clientId = client.id;
                publishers.add(save(phone));    
            });
            if ( dto.contact.isPresent()) {
                Contact c = dto.contact.get();
                c.clientId = client.id;
                publishers.add(save(c));
            }
            if ( dto.referral.isPresent()) {
                Referral r = dto.referral.get();
                r.clientId = client.id;
                publishers.add(save(r));
            }
            if ( publishers.size() > 0 )
                return Mono.zip(publishers, obs->client.id);
            else
                return Mono.just(client.id);
        });
    });
}

I ran this with the following example code:我使用以下示例代码运行它:

@Override
public void run(ApplicationArguments args) throws Exception {
    saveClient(new Client(), null, null, null).subscribe(System.out::println);
    saveClient(new Client(), new Phone(), null, null).subscribe(System.out::println);
    saveClient(new Client(), new Phone(), new Contact(), null).subscribe(System.out::println);
    saveClient(new Client(), new Phone(), new Contact(), new Referral()).subscribe(System.out::println);
}


private Mono<Long> saveClient(Client client, Phone phone, Contact contact,
        Referral referral) {
    // TODO Auto-generated method stub
    DTO dto = new DTO();
    dto.client = client;
    dto.phones = new ArrayList<>();
    if ( phone != null ) dto.phones.add(phone);     
    dto.contact = Optional.ofNullable(contact);
    dto.referral = Optional.ofNullable(referral);
    return doWork(Mono.just(dto));
}

So, this uses the Mono.zip trick.因此,这使用了Mono.zip技巧。 The saved client is flatmapped so that is done first.保存的客户端是平面映射的,因此首先完成。 Then a list of monos is created for all subsequent saves that need to be done.然后为所有需要完成的后续保存创建一个单声道列表。 These monos are all executed asynchronously by the Mono.zip function.这些单声道都由 Mono.zip function 异步执行。 The "combiner" function does nothing with the results, it just returns the clientId which is what is wanted for the client. “组合器” function 对结果不做任何事情,它只返回客户端所需的 clientId。 The Mono.zip combines all the Monos into a single Mono to return to the client. Mono.zip 将所有 Mono 组合成一个 Mono 以返回给客户端。 In a sense this is just taking procedural code and wrapping it in the reactive library rather than getting overly focused on functional programming.从某种意义上说,这只是采用程序代码并将其包装在反应库中,而不是过度关注函数式编程。 This is easy to read and modify if the business "process" changes going forward.如果业务“流程”发生变化,这很容易阅读和修改。

This is a starting point if you like it.如果您喜欢,这是一个起点。 I didn't use Repository::saveAll so that could be an improvement.我没有使用Repository::saveAll所以这可能是一个改进。

It's important to be sure all your Flux and Mono publishers are chained together.确保所有FluxMono发布者都链接在一起很重要。 In your final example you seemed to be dropping them.在您的最后一个示例中,您似乎正在放弃它们。 Simply creating them is not enough, they all have to be returned to the client somehow.仅仅创建它们是不够的,它们都必须以某种方式返回给客户端。 Also, your code has a subscribe call which is a no-no.此外,您的代码有一个禁止的subscribe调用。 Only the client should have subscribe.只有客户端应该订阅。 I think you should have used a map there.我认为您应该在那里使用map

EDIT: Fixed a bug.编辑:修复了一个错误。 Check your code carefully.仔细检查您的代码。

a plain if-statement can be done for instance in a flatMap and then acted upon.例如,可以在flatMap中完成一个普通的 if 语句,然后对其进行操作。

public Mono<String> foobar() {
    return Mono.just("foo").flatMap(value -> {
        if(value != null)
            return Mono.just("Has value");
        else
            return Mono.empty();
    }
}

foobar()
    .switchIfEmpty(Mono.just("Is empty"))
    .subscribe(output -> System.out.println(output);

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

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