【问题标题】:Handling DataIntegrityViolationExceptions in Spring Data REST在 Spring Data REST 中处理 DataIntegrityViolationExceptions
【发布时间】:2017-04-28 10:44:42
【问题描述】:

假设我有带有属性的机场实体:

@Column(nullable = false, unique = true)
private final String code;

显然,当我使用之前使用的代码持久化实体时,它会抛出 DataIntegrityViolationException

我有启用 Spring Data REST 的 Spring Boot 应用程序。

对于这种情况,有RepositoryRestExceptionHandler处理DataIntegrityViolationExceptions并将其映射到409状态码:

@ExceptionHandler({ OptimisticLockingFailureException.class, DataIntegrityViolationException.class })
ResponseEntity<ExceptionMessage> handleConflict(Exception o_O) {
    return errorResponse(HttpStatus.CONFLICT, new HttpHeaders(), o_O);
}

典型响应如下所示:

HTTP/1.1 409
X-Application-Context: application
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 28 Apr 2017 10:21:31 GMT

{
  "cause" : {
    "cause" : {
      "cause" : null,
      "message" : "Unique index or primary key violation: \"UK_X9RMMVEVTW274QQXGT73OQ74_INDEX_C ON PUBLIC.AIRPORT(CODE) VALUES ('null', 54)\"; SQL statement:\ninsert into AIRPORT (id, rec_version, code, description) values (null, ?, ?, ?) [23505-194]"
    },
    "message" : "could not execute statement"
  },
  "message" : "could not execute statement; SQL [n/a]; constraint [\"UK_X9RMMVEVTW274QQXGT73OQ74_INDEX_C ON PUBLIC.AIRPORT(CODE) VALUES ('null', 54)\"; SQL statement:\ninsert into AIRPORT (id, rec_version, code, description) values (null, ?, ?, ?) [23505-194]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement"
}

问题是使用 SQL 异常来响应响应是否是一种好方法。在我看来不是。

如果没有,有哪些好的做法可以遵循以及如何在引导和数据 REST 中实现它们?

【问题讨论】:

  • 正确的解决方案是在通过 Spring Validator 实现或通过自定义 JSR303 注释进入数据库之前应用您自己的验证。
  • 在这种情况下,验证逻辑必须进行数据库查找以查看具有给定代码的机场是否已经存在。这是好方法吗?性能明智?基于此评论,不推荐使用 JSR303:github.com/spring-projects/spring-boot/issues/…
  • 除了数据库查找之外,您还打算怎么做呢。您不能让它在 DB 提交时失败,因为这不会让您产生 有意义 错误消息:没有客户端会希望看到您的 SQL 异常。

标签: spring-boot spring-data-rest


【解决方案1】:

用 SQL 异常处理响应是否是一种好方法?

在大多数情况下,最好将此类异常转换为自定义错误消息/错误代码,然后再发送到客户端应用程序。

有哪些好的做法可以遵循以及如何在引导和数据 REST 中实施?

在 spring-boot & REST 中,你可以实现你自己的 ExceptionController/ExceptionHandler 来处理这种类型的异常。在这里你可以捕获任何类型的异常,并且可以将其转换为任何形式的自定义对象。

例如:

@ControllerAdvice
@RestController
public class ExceptionController {

@ExceptionHandler(value = Exception.class)
public ResponseEntity<?> handleException(Exception e) {
    UserResponse response = null;

    if(e instanceof DataIntegrityViolationException){
        DataIntegrityViolationException ex = (DataIntegrityViolationException) e;
        response = new UserResponse(ErrorCodes.DuplicateMobNo, "This mobile no is already Registered!");
        return new ResponseEntity<UserResponse>(response, HttpStatus.CONFLICT);
    }
}

UserResponse 可以是这样的:

public class UserResponse extends GenericResponse{

    private Long customErrorCode;
    private String errorMsg;

public UserResponse() {

}

    public UserResponse(Long code, String msg) {
        this.customErrorCode = code;
        this.errorMsg = msg;
    }

    public String getCustomErrorCode () {
        return customErrorCode ;
    }

    public void setCustomErrorCode (Long customErrorCode ) {
        this.customErrorCode = customErrorCode ;
    }

    public String getErrorMsg  () {
        return errorMsg ;
    }

    public void setErrorMsg  (String errorMsg ) {
        this.errorMsg = errorMsg ;
    }
}

您可以创建自己的自定义错误代码,例如 DuplicateMobileNo 等

【讨论】:

  • 您如何确定错误与哪个字段相关?
  • 这个例子太具体了,不能普遍适用于所有的例子。
【解决方案2】:
@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<BaseResponse> handleException(DataIntegrityViolationException ex, WebRequest request) {
    log.error("DataIntegrity Violation Exception ::: {}", ex);
    
    String message = ex.getMostSpecificCause().getMessage();

    if (message != null) {
        message = message.split("for")[0];
    } else {
        message = ResponseCodeEnum.R_66.getRespDescription();
    }
    
    BaseResponse baseResponse = new BaseResponse();
    baseResponse.setResponseCode(ResponseCodeEnum.R_66.getRespCode());
    baseResponse.setResponseDescription(message);

    return new ResponseEntity<>(baseResponse, HttpStatus.BAD_REQUEST);
}

此解决方案并不完美,但它提取了导致异常的主要值,您可以将其返回给用户。这背后的想法是,至少他们能够知道导致问题的确切值并使用然后他们知道要更改或调整的值。

【讨论】:

    猜你喜欢
    • 2021-09-06
    • 2018-08-22
    • 2016-08-20
    • 2016-03-12
    • 2016-10-26
    • 2018-02-03
    • 2017-11-28
    • 2014-02-04
    • 1970-01-01
    相关资源
    最近更新 更多