WebFlux: зачем мне использовать flatMap в CRUD - PullRequest
0 голосов
/ 23 февраля 2020

Я нашел примеры на Inte rnet, но это не дает мне полного понимания. Стандартный CRUD при использовании WebFlux.

Маршрутизатор:

@Configuration
public class PersonRouter {

    @Bean
    public RouterFunction<ServerResponse> route(PersonHandler handler) {
        return RouterFunctions
                .route(GET("/getAllPersons").and(accept(MediaType.APPLICATION_JSON)), handler::findAll)
                .andRoute(GET("/getPerson/{id}").and(accept(MediaType.APPLICATION_STREAM_JSON)), handler::findById)
                .andRoute(POST("/createPerson").and(accept(MediaType.APPLICATION_JSON)), handler::save)
                .andRoute(DELETE("/deletePerson/{id}").and(accept(MediaType.APPLICATION_JSON)), handler::delete);
    }

}

Обработчик:

@Component
public class PersonHandler {

    private final PersonService personService;

    public PersonHandler(PersonService personService) {
        this.personService = personService;
    }

    public Mono<ServerResponse> findById(ServerRequest request) {
        String id = request.pathVariable("id");
        return ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(personService.getById(id), Person.class);
    }

    public Mono<ServerResponse> findAll(ServerRequest request) {
        return ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(personService.getAll(), Person.class);
    }

    public Mono<ServerResponse> save(ServerRequest request) {
        final Mono<Person> person = request.bodyToMono(Person.class);
        return ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(fromPublisher(person.flatMap(personService::save), Person.class));
    }

    public Mono<ServerResponse> delete(ServerRequest request) {
        String id = request.pathVariable("id");
        return ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(personService.delete(id), Void.class);
    }

}

Репозиторий:

@Repository
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
}

Сервис:

@Service
@Transactional
@AllArgsConstructor
public class PersonService {

    private final PersonRepository personRepository;

    public Flux<Person> getAll() {
        return personRepository.findAll().switchIfEmpty(Flux.empty());
    }

    public Mono<Person> getById(final String id) {
        return personRepository.findById(id);
    }

    public Mono update(final String id, final Person person) {
        return personRepository.save(person);
    }

    public Mono save(final Person person) {
        return personRepository.save(person);
    }

    public Mono delete(final String id) {
        final Mono<Person> dbPerson = getById(id);
        if (Objects.isNull(dbPerson)) {
            return Mono.empty();
        }
        return getById(id).switchIfEmpty(Mono.empty()).filter(Objects::nonNull).flatMap(personToBeDeleted -> personRepository
                .delete(personToBeDeleted).then(Mono.just(personToBeDeleted)));
    }
}

Я понимаю все, кроме методов save и update. Я не понимаю, почему мы используем flatMap в этой ситуации. Почему это так, и как я могу написать реализацию update в моем Handler .

Обновлено

Давайте посмотрим на метод save () в обработчике

public Mono<ServerResponse> save(ServerRequest request) {
        final Mono<Person> person = request.bodyToMono(Person.class);
        return ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(fromPublisher(person.flatMap(personService::save), Person.class));
    }

Я думаю, дело в том, что мы уже получили:

final Mono<Person> person = request.bodyToMono(Person.class);

, а затем мы делаем:

personService::save

В результате мы получаем Mono >

flatMap похож на map, за исключением того, что он распаковывает возвращаемое значение лямбда-выражения, если само значение содержится в Publisher<T> , В нашем случае метод personService.save(T) возвращает Mono<T>. Если бы мы использовали карту вместо flatMap(T), у нас было бы Mono< Mono< T>>, тогда как мы действительно хотим Mono<T>. Мы можем чисто решить эту проблему, используя flatMap.

Я прав или это утверждение неверно?

1 Ответ

1 голос
/ 26 февраля 2020

зачем вам flatMap.

Это мои идеи, ответ варьируется в зависимости от того, работает ли он на Mono или Flux.

1.

javado c карты методов и flatMap показывает их использование:

map: Преобразуйте элемент, испускаемый этим {@link Mono}, применяя к нему синхронную функцию.

flatMap: преобразует элемент, испускаемый этим {@link Mono}, асинхронно, возвращая значение, испускаемое другим {@link Mono} (возможно, изменяя тип значения).

То говорит, что, рассматривая flatMap и map как конвейер с вводом и выводом, вы используете map, когда вывод - тот же элемент, в противном случае используйте flatMap. Проверьте это:

public Mono<ServerResponse> influCRUD(ServerRequest req) {
    return req.bodyToMono(S.class) // the pipline begins with S class.
       .map(s -> {s.setF1(f1); s.setF2(f2); return s;}) // the pipeline has the same intput and output, i.e. object s, you use map.
       .flatMap(s -> webClient // the pipeline has S input, and T output, you use flatMap
            .post()
            .uri(uri)
            .body(BodyInserters.fromObject(s))
            .retrive()
            .bodyToMono(T.class) 
        ).flatMap(t -> ServerResponse // now the pipeline changes again, you use flatMap.
           .ok()
           .contentType()
           .body(BodyInserters.fromObject(t))
        );
}

стоит упомянуть, что map может иметь другой объект в качестве вывода.

flatMap обрабатывает все элементы

указанная выше причина полезна для Mono производителя. Для Flux, flatMap обрабатывает все элементы, а map обрабатывает все элементы (или один элемент). это то же самое, что и в лямбде. Если вы хотите обработать каждый элемент, вы используете flatMap.

flatMap снимает один слой моно для вас.

Посмотрите на их объявление:

<R> Mono<R> map(Function<? super T, ? extends R> mapper)

и

<R> Mono<R> flatMap(Function<? super T, ? extends Mono<? extends R>> transformer)

Function ничего не делает но a -> b, когда b является выходом другого Producer/Subsciber (это очень вероятно, если вы используете реактивное программирование), например, часть webClient в предыдущем примере, она имеет форму Mono или Flux. Используя flatMap, он возвращает Mono<R> для вас, где map возвращает Mono<Mono<R>>, как они указаны в объявлении функции.

Я тоже новичок в reative программировании, более чем приветствую исправьте это .

...