0

I am trying to build an application using reactive. Initially i used to get proper response but after modifying the code, the record is not persisted into Database, but when I changed the logic to modify the response body, I see a success response but no record was found in DB nor I can see an error in logs.

Code before Modifying:

public Mono<ServerResponse> createCustomer(ServerRequest serverRequest) {
        return serverRequest.bodyToMono(Customer.class).flatMap(customer -> {
            ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(customerRepository.save(customer), Customer.class);

        });
    }

But I want to return a generic API Response for all API calls and modified the code as below:

public Mono<ServerResponse> createCustomer(ServerRequest serverRequest) {
        Response response = new Response();
        return serverRequest.bodyToMono(Customer.class).flatMap(customer -> {
            saveCustomer(customer,apiResponse);
            return ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(Mono.just(apiResponse), Response.class);

        }).doOnError(err -> {
                    log.error("Exception while creating customr record", err);
        }).onErrorResume(err ->  {
            apiResponse.setError(new Error(err.getMessage(),err.getCause()));
            return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(Mono.just(apiResponse), ApiResponse.class);
        });
    }

    public Response saveCustomer(Customer customer,Response apiResponse){
        customerRepository.save(customer);
        apiResponse.setCode("0");
        apiResponse.setMessage("Successfully Created customer");
        return apiResponse;
    }

Any thoughts are appreciated please.

ging
  • 219
  • 7
  • 20

2 Answers2

1

I doubt this code - customerRepository.save(customer);

If you are using R2DBC, the save method does not save directly. It will return a publisher type. It has to be subscribed to make it work. Otherwise it will not insert records.

You need to do this!

customerRepository.save(customer).subscribe();

However this is NOT a good practice to directly subscribe like this. Instead you should do something like this.

public Mono<Response> saveCustomer(Customer customer,Response apiResponse){
    return customerRepository.save(customer)
             .map(c -> {
                  apiResponse.setCode("0");
                  apiResponse.setMessage("Successfully Created customer");
                  return apiResponse;
              });
}

then modify your

return serverRequest.bodyToMono(Customer.class).flatMap(customer -> {.....

code to something like this.

return serverRequest.bodyToMono(Customer.class)
                     .flatMap(customer -> saveCustomer(customer, response))
                     .flatMap(r -> ServerResponse.ok()
                                 .contentType(MediaType.APPLICATION_JSON)
                                 .body(Mono.just(r), Response.class))
vins
  • 15,030
  • 3
  • 36
  • 47
  • That was it. But still trying to understand why .save didnt throw any error if the record was not persisted. Any docs that can explain this? I am fairly new to webflux and trying to gain some knowledge by building a sample application. @vins. Thanks for the help. – ging Jul 26 '21 at 23:47
  • 1
    Hi.. It will not throw any error. Save action will not even be performed until someone subscribes it. Do not mistake me if I say this - do not do anything in webflux before you learn `reactive programming`. you must be comfortable with reactor library first. https://www.vinsguru.com/topics/ - check webflux section – vins Jul 27 '21 at 01:42
  • Thanks. As said i was just learning reactive. Thanks for the suggestion, will understand a bit better before moving further. – ging Jul 27 '21 at 04:34
  • 1
    I believe the topics are clear with examples in your tutorial. Thanks for sharing. – ging Jul 28 '21 at 00:03
0

Are you using a reactive JDBC Driver like R2DBC, and also the reactive Repository ? If not it better you should as it will make complete stack reactive . You can use R2DBC drivers maven and Spring Data R2DBC for Reactive Repository

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>

<dependency>
    <groupId>dev.miku</groupId>
    <artifactId>r2dbc-mysql</artifactId>
    <version>0.8.2.RELEASE</version>
</dependency>

and then you will have to use connection factory using r2dbc connection instead of JDBC something like this.

@Bean
  public ConnectionFactory connectionFactory() {
    ConnectionFactory connectionFactory = ConnectionFactories.get(
        "r2dbcs:mysql://localhost:3306/dbname?"+
            "zeroDate=use_round&"+
            "sslMode=disabled");

    return connectionFactory;
  }

One this configuration is done you can extend ReactiveCrudRepository from Spring Data R2DBC and create you own repository something Like this

public interface UserRepository extends ReactiveCrudRepository<User, Long> {

  @Query("SELECT * FROM user WHERE firstname = :firstname")
  Flux<User> findByFirstName(String firstname);
}

Please check this blog for more info.

Tejas Garde
  • 337
  • 2
  • 13