Spring Data JPA - how to implement proper exception handling?

Viewed 2910

I am using Spring Data JPA to process database calls. For this purpose, I have created:

  1. An EmployeeRepository interface, which extends a JpaRepository<Employee, Long>

  2. An EmployeeService, which defines three methods:

     Employee saveEmployee(Employee employee);
     Optional<Employee> getEmployee(Long id);
     Long deleteEmployee(Long id);
    
  3. An implementation of the EmployeeService:

     @Override
     public Employee saveEmployee(Employee employee) {
         return employeeRepository.save(employee);
     }
    
     @Override
     public Optional<Employee> getEmployee(Long id) {
         return employeeRepository.findEmployeeById(id);
     }
    
     @Override
     public Long deleteEmployee(Long id) {
         employeeRepository.deleteById(id);
         return id;
     } 
    

The issue is the following:

The get-methods work fine and can return an optional. The save-method, on the other hand, cannot return an optional. Apparently the JpaRepository returns an instance of the saved object upon calling save(). I would rather return an optional, since something could go wrong when saving the employee and in that case, I would like to throw an error - i.e. whenever the optional is not present, I throw an error.

The same holds for the delete-operation: What, for example, if I ask to delete an employee and pass in an id, which does not exist? I would like to catch this error and only then return the passed in id, if the delete operation was successfull. Which error would I have to catch for this purpose? Can someone explain this to me?

=================================================

update:

  • I have fixed the problem with the delete-call by simply checking if the given employee-id exists before calling `deleteById(id); if it doesn't, the service returns null, if it does, it return the id. The controller looks like this:

      @DeleteMapping("/{id}")
      public ResponseEntity<Long> deleteEmployee(@PathVariable Long id) {
          Long deletedEmployeeId = employeeService.deleteEmployee(id);
          if (deletedEmployeeId != null) {
              return ResponseEntity.ok(deletedEmployeeId);
      } else {
          return ResponseEntity.status(HttpStatus.BAD_REQUEST);
      }
    

I am missing the DataAccessException, however. So, can it be that I would actually have to do the following:

    @DeleteMapping("/{id}")
    public ResponseEntity<Long> deleteEmployee(@PathVariable Long id) {
        try {
            Long deletedEmployeeId = employeeService.deleteEmployee(id);
            if (deletedEmployeeId != null) {
                return ResponseEntity.ok(deletedEmployeeId);
            } else {
                return ResponseEntity.status(HttpStatus.BAD_REQUEST);
            }
         } catch (DataAccessException e) {
             return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
         }

This looks a bit like an over-kill to be honest.

  • I am still a bit unsure how to deal with the save-call. Before I posted this question, my controller was simply doing the following:

      @PostMapping
      public ResponseEntity<Employee> saveEmployee(@RequestBody Employee employee) {
          return ResponseEntity.ok(employeeService.saveEmployee(employee));
      }
    

What happens, if employeeService.saveEmployee(employee) throws a DataAccessException? Am I still returning a HTTP-status-code of 200, as I wrap the response in an ResponseEntity.ok() ?

If so, I would suggest to do the following:

    @PostMapping
    public ResponseEntity<Employee> saveEmployee(@RequestBody Employee employee) {
        try {
            Employee savedEmployee = employeeService.saveEmployee(employee);
            return ResponseEntity.ok(savedEmployee);
        } catch (DataAccessException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

Is this something people do? Or are DataAccessExceptions usually neglected as they are not expected?

1 Answers

The method "save" always returns you the same object you are going to save. Only by checking the "id", you can see if the object is saved or not. But if an error occurs in the database, an exception will be thrown and you can catch it by putting "employeeRepository.save(employee)" in a try-catch block. The same way you can do for deleteById

Related