Skip to content
Advertisement

Spring Webflux – reactive repository saveAll(Iterable) vs saveAll(Publisher)

Small question about the webflux reactive repository, especially about the methods saveAll Flux saveAll(Iterable var1); versus Flux saveAll(Publisher var1);

Wanted to compare, I wrote the following:

@Controller
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

    @Autowired
    private SomeReactiveRepository someReactiveRepository;

    @PostMapping(path = "/saveListInsideMono", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<QuestionResponse> saveListInsideMono(@RequestBody Mono<QuestionRequest> questionRequestMono) {
        //just doing some business transformation on the list inside the mono
        Mono<List<String>> enhancedStringListMono = questionRequestMono.map(questionRequest -> enhance(questionRequest));
        //take the pojo inside the mono and map it to a saveAllAndConvertToResponse method (see next method)
        Mono<QuestionResponse> questionResponseMono = enhancedStringListMono.map(enhancedStringList -> saveAllAndConvertToResponse(enhancedStringList));
        return questionResponseMono;
    }

    private QuestionResponse saveAllAndConvertToResponse(List<String> enhancedStringList) {
        // use the repository <S extends T> Flux<S> saveAll(Iterable<S> var1); + subscribe
        return someReactiveRepository.saveAll(enhancedStringList).thenReturn(new QuestionResponse(enhancedStringList));
    //this also works but not good to subscribe
        //someReactiveRepository.saveAll(enhancedStringList).subscribe();
        //return new QuestionResponse(enhancedStringList);
    }

    @PostMapping(path = "/saveFlux", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<QuestionResponse> saveFlux(@RequestBody Mono<QuestionRequest> questionRequestMono) {
        //just doing some business transformation on the list inside the mono
        Mono<List<String>> enhancedStringListMono = questionRequestMono.map(questionRequest -> enhance(questionRequest));
        // use the repository <S extends T> Flux<S> saveAll(Publisher<S> var1); to save the flatMapMany + fromIterable directly
        Flux<String> enhancedStringFlux = someReactiveRepository.saveAll(enhancedStringListMono.flatMapMany(Flux::fromIterable));
        Mono<QuestionResponse> questionResponseMono = enhancedStringFlux.collectList().map(enhancedString -> convertToResponse(enhancedString));
        return questionResponseMono;
    }

    private QuestionResponse convertToResponse(List<String> enhancedStringList) {
        //return the object needed
        return new QuestionResponse(enhancedStringList);
    }

    private static List<String> enhance(QuestionRequest questionRequest) {
        //dummy business transformation logic
        List<String> baseList = questionRequest.getList();
        List<String> enhancedList = baseList.stream().map(oneString -> "enhanced" + oneString).collect(Collectors.toList());
        return enhancedList;
    }

    public class QuestionRequest {
        private List<String> list;

        public List<String> getList() {
            return list;
        }
    }

    public class QuestionResponse {
        private List<String> enhancedList;

        public QuestionResponse(List<String> enhancedList) {
            this.enhancedList = enhancedList;
        }
    }

}

In terms of “correctness” both codes are doing what is expected. Everything is persisted successfully.

But in terms of performance, reactive paradigm, IO Utilisations to DB, Netty Core usage, what is the “best” solution and why please?

Thank you

Advertisement

Answer

It all depends on what objects you currently have. If you have a Flux of objects, use the saveAll method that takes a Publisher. If you have the actual Collection of objects, use the saveAll method that takes an Iterable.

As an example, if you look at the implementation SimpleReactiveCassandraRepository the implementation of saveAll that takes an Iterable just wraps it in a Flux and delegates to the saveAll method that accepts a Flux

public <S extends T> Flux<S> saveAll(Iterable<S> entities) {

    Assert.notNull(entities, "The given Iterable of entities must not be null");

    return saveAll(Flux.fromIterable(entities));
}

As a result, there should no difference in terms of IO utilisation or netty core usage. Also, both follow the reactive paradigm.

SimpleReactiveCassandraRepository Code

Advertisement