簡體   English   中英

如何將 if-else 語句包含到反應流中

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

我有一個 Spring Webflux 反應式服務,它接收 DTO 並將其插入到多個表中。 有時我們可能需要根據傳入的 DTO 跳過插入某些表。

這些是要求:

  1. 創建一個新客戶端
  2. 如果 DTO 中存在客戶端引薦,則創建新的客戶端引薦。
  3. 如果存在於 DTO 中,則創建客戶端次要聯系人
  4. 創建客戶端電話(如果存在於 DTO 中)

問題:-

  1. 不確定如何在反應流中應用 if 條件。
  2. 有沒有更好的方法來做到這一點?
  3. 在這里,除了第一個之外,所有其他操作都可以並行運行。
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() 可以是 null,
client.getSecondary() 可以是 null,client.getPhones() 可以為空。

我用 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());
                            });
                });
            });
        });
    }

在這里, createClientWithPhones 是強制性的,所以如果檢查那里沒有。 但是其他 2 種方法 createReferral 和 createSyContact 有 if 檢查。 需要先執行createClientWithPhones,它會返回clientId。 此 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();
        
    }

這是處理這個問題的方法嗎?

好吧,我認為一般來說對反應庫沒有很好的理解。 我的意思是,通常人們會接近 Java 8 個流,因為他們正在嘗試進行函數式編程。 當然,響應式庫是基於函數式編程的,但我認為其目的是圍繞阻塞 I/O 進行異步處理。 考慮 WebFlux 項目的(當前)首頁。

什么是反應式處理? 反應式處理是一種范式,使開發人員能夠構建可以處理背壓(流量控制)的非阻塞、異步應用程序。

因此,這是一種冗長的說法,我認為最好關注 I/O 發生的位置,而不是創建功能代碼。 如果你需要if語句,那么你需要if語句。 與其試圖找出如何使用函數式編程的if語句,不如嘗試找出 I/O 發生的位置並以異步方式處理它。 我喜歡使用的一個“技巧”是Mono::zipFlux::zip 這些函數將許多 I/O 調用組合到一個發布者中以返回給客戶端。 因此,請考慮此示例代碼。

讓我們創建一些響應式 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);
}

我們需要一些示例類來使用:

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

我們的輸入可能是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);
        });
    });
}

我使用以下示例代碼運行它:

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

因此,這使用了Mono.zip技巧。 保存的客戶端是平面映射的,因此首先完成。 然后為所有需要完成的后續保存創建一個單聲道列表。 這些單聲道都由 Mono.zip function 異步執行。 “組合器” function 對結果不做任何事情,它只返回客戶端所需的 clientId。 Mono.zip 將所有 Mono 組合成一個 Mono 以返回給客戶端。 從某種意義上說,這只是采用程序代碼並將其包裝在反應庫中,而不是過度關注函數式編程。 如果業務“流程”發生變化,這很容易閱讀和修改。

如果您喜歡,這是一個起點。 我沒有使用Repository::saveAll所以這可能是一個改進。

確保所有FluxMono發布者都鏈接在一起很重要。 在您的最后一個示例中,您似乎正在放棄它們。 僅僅創建它們是不夠的,它們都必須以某種方式返回給客戶端。 此外,您的代碼有一個禁止的subscribe調用。 只有客戶端應該訂閱。 我認為您應該在那里使用map

編輯:修復了一個錯誤。 仔細檢查您的代碼。

例如,可以在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