Home > other >  Using Optional<T> in Spring Boot Service & Controller?
Using Optional<T> in Spring Boot Service & Controller?

Time:07-12

I use Optional in my Spring Boot project, but I am not sure if I also need to pass it to Controller as return type. So, could you pls clarify me about the following issues?

Here is the example code to describe my question better:

repository:

Optional<Product> findByCode(String code);

service:

public Optional<Product> findByCode(String code) {
    return productRepository.findByCode(code)
            .orElseGet(Optional::empty);
}

controller:

@GetMapping("/products/{code}")
public ResponseEntity<Optional<Product>> findByCode(@PathVariable String code) {

    Optional<Product> product = productService.findByCode(code);
    if (product.isPresent()) {
        return new ResponseEntity<>(product, HttpStatus.OK);
    }
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

1. I think we use Optional, when the result may be empty. For example, in the previous situation, there may be no product for the given code. Is that all, or may there be some other common examples that make us to use of Optional?

2. In the given example, I try to use something in the service method, but it is not working. But I really have no idea if I should use Optional as return value to the Controller. So, could you give a proper service and Controller method example for this service method?

CodePudding user response:

You don't return an optional from endpoints, you return the entity or dto. You could use the optional like that:

@GetMapping("/products/{code}")
public ResponseEntity<Product> findByCode(@PathVariable String code) {
  Optional<Product> product = productService.findByCode(code);
  return product.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.noContent().build());
}

Few notes about your code:

  1. Your service does not make much sense - .orElseGet(Optional::empty) is pointless, if the optional is empty, return empty optional. You can just do:
return productRepository.findByCode(code);

and that's all.

  1. It makes no sense to return code no content(204) when the requested resource is not found. The logical response code would be not found(404). For your code, like this:
return product.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());

CodePudding user response:

repository: No change

Optional<Product> findByCode(String code);

service: .orElseGet(Optional::empty); <= Useless

public Optional<Product> findByCode(String code) {
    return productRepository.findByCode(code);
}

controller:

@GetMapping("/products/{code}")
public ResponseEntity<Product> findByCode(@PathVariable String code) {
    return productService.findByCode(code).map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.noContent().build());
}

CodePudding user response:

You don't need to pass Optional to Controller as a return type. when you don't even need to declare service method as return type optional. when you use the orElseGet method like

productRepository.findByCode(code)
            .orElseGet(Optional::empty);

you are converting your result to product data if it's available or to empty data type of product. You can declare your service method as:

public Product findByCode(String code) {
    return productRepository.findByCode(code)
            .orElseGet(Optional::empty);
}

and your controller as:

@GetMapping("/products/{code}")
public ResponseEntity<Product> findByCode(@PathVariable String code) {

    Product product = productService.findByCode(code);
    if (product.isPresent()) {
        return new ResponseEntity<>(product, HttpStatus.OK);
    }
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

But if you want to return the optional type then you should remove orElseGet from service method and make it like:

public Optional<Product> findByCode(String code) {
    return productRepository.findByCode(code)
}

CodePudding user response:

Hii generally optional was introduced to avoid working with null values. If we really want to assign a not existing value to a variable, we should use Optional so that we can prevent null pointer exceptions. And it is not mandatory to give it to a controller. You can give like this also if we don't want to give optional to the controller.

@GetMapping("/products/{code}")
public ResponseEntity<Product> findByCode(@PathVariable String code){
    Optional<Product> products=productService.findProductById(String);
    if(products.isPresent()){
        Product product=products.get();
        return new ResponseEntity<>(product,HttpStatus.OK);
    }
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

And you can give your service method like this

public Optional<Product> findProductByCode(String code) {
return productRepository.findProductByCode(code);

}

  • Related