I found examples on the Internet but this doesn't give me a full understanding. Standard CRUD when using WebFlux.
Router:
@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);
}
}
Handler:
@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:
@Repository
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
}
Service:
@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)));
}
}
I understand everything except the save
and update
methods. I don't understand why we use flatMap
in this situation.
Why is this so, and how can I write the implementation of the update method in my Handler.
Updated
Let's see the method save() in Handler
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));
}
I think the fact is that we have already received:
final Mono<Person> person = request.bodyToMono(Person.class);
and then we do:
personService::save
As a result, we get Mono< Mono< Person>>
flatMap is just like map, except that it unpacks the return value of the lambda given if the value is itself contained in a Publisher<T>
. In our case, the personService.save(T)
method returns a Mono<T>
. If we’d used map instead of flatMap(T)
, we’d have a Mono< Mono< T>>
, when what we really want is a Mono<T>
. We can cleanly solve this problem using flatMap.
Am I right or is this statement wrong?
Publisher
you pass tobody
method – Simon Baslé