【问题标题】:How catch hibernate/jpa constraint violations in Spring Boot?如何在 Spring Boot 中捕获 hibernate/jpa 约束违规?
【发布时间】:2019-04-08 01:36:29
【问题描述】:

我一直无法捕捉到 ConstraintViolationException(或 DataIntegrityViolationException) 响应实体异常处理程序。 我想在响应中返回 jpa 失败(例如违反了哪个约束)。 (我不喜欢在方法参数上使用@Valid 并捕获handleMethodArgumentNotValid)。

...
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.dao.DataIntegrityViolationException;

@ControllerAdvice
public class PstExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({ConstraintViolationException.class})
    public ResponseEntity<Object> handleConstraintViolation(
        ConstraintViolationException ex, WebRequest request) {

        ...

        return new ResponseEntity<Object>(...);
    }
}

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
public class Student {

    @Id
    private Long id;

    @NotNull
    private String name;

    @NotNull
    @Size(min=7, message="Passport should have at least 7 characters")
    private String passportNumber;

    public Student() {
    }

...

}

@RequestMapping(value = "/addstudent"...)
@ResponseBody
public ResponseEntity<Object> addStudent(@RequestBody StudentDto studentDto) {

    Student student = new Student();
    student.setId(studentDto.getId());                          // 1L
    student.setName(studentDto.getName());                      // "helen"
    student.setPassportNumber(studentDto.getPassportNumber());  // "321"

    studentRepository.save(student);

    return ResponseEntity.accepted().body(student);
}

谢谢...

【问题讨论】:

  • 你能试试 HibernateException 吗?只需更新此 @ExceptionHandler({HibernateException.class}) 并让我知道您的答案是什么......?
  • 谢谢 - 虽然它没有解决问题。问题仍然存在。
  • 哦,对了,我看了一下,并没有考虑像 ConstraintViolationException 这样的异常,因为它们是我认为的 HIbernateException 的扩展。所以 PersistenceException 而不是从 HibernateException 扩展的 AnyException 被抛出。所以我认为你应该更新这样的东西:@ExceptionHandler({PersistenceException.class}) public ResponseEntity handleConstraintViolation(PersistenceException ex, WebRequest request) { if (ex instanceof ConstraintViolationException) { ConstraintViolationException consEx = (ConstraintViolationException) ex;跨度>
  • 我在哪里/如何投票?谢谢
  • 只需单击向上按钮此评论 xD 或有用的 cmets :)

标签: hibernate spring-mvc spring-boot spring-data-jpa


【解决方案1】:

这样的事情会有所帮助:

@ExceptionHandler(value = {DataIntegrityViolationException.class})
public ResponseEntity<String> handlePreconditionFailed(DataIntegrityViolationException exception) {
        LOG.error(ERROR_MESSAGE, exception);
        return ResponseEntity.status(HttpStatus.CONFLICT).build();
}

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,所以我不得不捕获 JDBCException 异常:

    @ExceptionHandler({JDBCException.class})
    public ResponseEntity<Object> handleConstraintViolation(TransactionSystemException ex, WebRequest request) {
    
        if (ex.getCause() instanceof ConstraintViolationException) {
            ...
        }   
    
        return new ResponseEntity<Object>(...);
    }
    

    【讨论】:

      【解决方案3】:

      您无法捕获 ConstraintViolationException.class,因为它不会传播到代码的该层,而是被较低层捕获、包装并在另一种类型下重新抛出。因此,遇到 Web 层的异常不是 ConstraintViolationException。所以你可以这样做:

      @ExceptionHandler({TransactionSystemException.class})
      protected ResponseEntity<Object> handlePersistenceException(final Exception ex, final WebRequest request) {
          logger.info(ex.getClass().getName());
          //
          Throwable cause = ((TransactionSystemException) ex).getRootCause();
          if (cause instanceof ConstraintViolationException) {        
      
              ConstraintViolationException consEx= (ConstraintViolationException) cause;
              final List<String> errors = new ArrayList<String>();
              for (final ConstraintViolation<?> violation : consEx.getConstraintViolations()) {
                  errors.add(violation.getPropertyPath() + ": " + violation.getMessage());
              }
      
              final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, consEx.getLocalizedMessage(), errors);
              return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
          }
          final ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred");
          return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
      }
      

      【讨论】:

        【解决方案4】:

        有3种方式:

        1. @Valid\@Validated 注释。

          一旦它们在参数级别使用,违规就可以通过方法注入的Errors \ BindingResult 类型实现获得。

        2. 每次将处于无效状态的实体传递给.save().persist() Hibernate 方法时,都会抛出ConstraintViolationException

        3. 可以通过注入的LocalValidatorFactoryBean.validate() 方法手动验证实体。然后可以通过传递的Errors 对象的实现获得约束违规。 究竟@Valid\@Validated注解是如何在内部实现的。

        【讨论】:

          【解决方案5】:

          不考虑 ConstraintViolationException 等异常,因为它们是从 HibernateException 扩展而来的。你可以看一下exceptions in convert method,这些都包含在 Hibernate Exceptions 中。

          @ExceptionHandler({TransactionSystemException.class})
          public ResponseEntity<Object> handleConstraintViolation(TransactionSystemException ex, WebRequest request) {
          
              if (ex.getCause() instanceof RollbackException) {
                 RollbackException rollbackException = (RollbackException) ex.getCause();
                   if (rollbackException.getCause() instanceof ConstraintViolationException) {
                       return new ResponseEntity<Object>(...);
                   }
              ...
              }
              ...
          
              return new ResponseEntity<Object>(...);
          }
          

          【讨论】:

            猜你喜欢
            • 2023-03-30
            • 2013-11-27
            • 2015-09-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-29
            • 2014-09-28
            相关资源
            最近更新 更多