如何在不阻塞的情况下在空 Mono 之后链接反应操作?

How to chain reactive operations after empty Mono without blocking?

基本上我想要实现的是调用第二个存储库(ReactiveCrudRepository)或抛出异常,具体取决于调用第一个存储库的结果。

我最初的想法是这样的:

/** Reactive with blocking code */
public Flux<SecondThing> getThings(String firstThingName) {
    FirstThing firstThing = firstRepo
        .findByName(firstThingName)
        // Warning: "Inappropriate blocking method call"
        .blockOptional()  // this fails in test-context
        .orElseThrow(() -> new FirstThingNotFound(firstThingName));

    return secondRepo.findAllByFirstThingId(firstThing.getId());
}

这将对应于以下非反应性方法:

/** Non-reactive */
public List<SecondThing> getThings(String firstThingName) {
    FirstThing firstThing = firstRepo
        .findByName(firstThingName)
        .orElseThrow(() -> new FirstThingNotFound(firstThingName));

    return secondRepo.findAllByFirstThingId(firstThing.getId());
}

我还没有找到以反应性非阻塞方式执行此操作的方法。如果第一次调用出现空 Mono ,我需要的只是抛出一个错误,如果不为空则继续管道;但我似乎无法在这里正确使用 onErrorStopdoOnError,并且 map 没有帮助,因为它跳过空的 Mono.

如果我使用 id 而不是 name,我有一个解决方法,但我对它不太满意,因为它在实例的情况下表现出不同的行为FirstThing 但没有 SecondThing 链接到它:

/** Reactive workaround 1 */
public Flux<SecondThing> getThings(Long firstThingId) {
    return secondRepo
        .findAllByFirstThingId(firstThingId)
        .switchIfEmpty(
            Flux.error(() -> new FirstThingNotFound(firstThingName))
        );
}

我发现的另一种解决方法如下,它将空 Mono 替换为 null 值,但它看起来不正确并且也会引发警告:

/** Reactive workaround 2 */
public Flux<SecondThing> getThings(String firstThingName) {
    return firstRepo
        .findByName(firstThingName)
        // Warning: "Passing 'null' argument to parameter annotated as @NotNull"
        .defaultIfEmpty(null)
        .flatMapMany(
            firstThing -> secondRepo.findAllByFirstThingId(firstThing.getId()
        )
        .onErrorMap(
            NullPointerException.class, e -> new FirstThingNotFound(firstThingName)
        );
}

什么是将调用链接到两个存储库的正确方法,以便 FirstThing 的存在或不存在与所请求的 firstThingName 条件调用第二个 repo?

我找到了一个非常简单的解决方案,我可能会因为没有早点找到它而感到羞愧:

public Flux<SecondThing> getThings(String firstThingName) {
    return firstRepo
        .findByName(firstThingName)
        .switchIfEmpty(Mono.error(() -> new FirstThingNotFound(firstThingName)))
        .flatMapMany(
            firstThing -> secondRepo.findAllByFirstThingId(firstThing.getId()
        );
}

诀窍在于 switchIfEmpty 不会强制您选择“有效”替换值,因此可以使用 Mono.error 直接传播正确的异常。