通过不存在的PK删除时如何处理Spring Data JPA删除?

问题描述

在使用 Spring 的 Jpa Repository deleteById(Long id) 方法时处理错误的最佳方法是什么?

认情况下,deleteById() 会检查 ID 是否在数据库中存在现有行,如果没有,则抛出 org.springframework.dao.EmptyResultDataAccessException,因为它期望行大小为 1。>

我首先尝试使用我的异常处理程序来处理这个异常,它工作正常,但是当 Spring 返回错误消息时,该消息向用户公开了我的包和类名。

@ExceptionHandler(EmptyResultDataAccessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<RestApiError> handleEmptyResultDataAccessException(EmptyResultDataAccessException ex,HttpServletRequest request) {
    RestApiError error = new RestApiError(HttpStatus.BAD_REQUEST,Map.of("message",ex.getMessage()),request.getRequestURI());
    return new ResponseEntity<>(error,error.getHttpStatus());
}

ex.getMessage() 返回:

没有类 net.demo.customerservice.model.CustomerLocation 存在 ID 为 7 的实体!

相反,我决定捕获 EmptyResultDataAccessException,然后在我调用 deleteById() 的地方抛出更多有用的异常和消息;

我当前的代码

public void delete(Long id) {
  try {
     repository.deleteById(id); // call Spring's Data JPA repository method deleteById
  } catch (EmptyResultDataAccessException ex) {
     throw new EntityNotFoundException("Location with ID: [" + id + "] was not found");
  }
}

这很好用,并向用户返回了一个很好的错误消息,但它看起来像一个黑客。

有没有更好的方法来处理 EmptyResultDataAccessException?我也可以在调用 delete 方法之前使用 existsById() 方法,但随后我使用了两个查询

解决方法

通常最好避免在整个代码中捕获异常。如果您可以将异常处理委托给另一个类,您就可以在一个地方一致地处理整个应用程序中的错误。您可以为此使用 @ControllerAdvice

@ControllerAdvice
class GlobalControllerExceptionHandler {

    @ExceptionHandler(EmptyResultDataAccessException.class)
    public ResponseEntity<> handleRecordNotFound(EmptyResultDataAccessException ex) {
        LOG.trace("Record not found: {}",ex.getMessage());
        RestApiError error = new RestApiError(HttpStatus.BAD_REQUEST,Map.of("message","Record not found"),request.getRequestURI());
        return new ResponseEntity<>(error,error.getHttpStatus());
    }
}

客户端知道它请求删除哪个实体,因此不需要在返回给客户端的错误消息中包含 id。您可以将带有他 id 的消息记录到日志文件中。