首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >SpringBoot不处理org.hibernate.exception.ConstraintViolationException

SpringBoot不处理org.hibernate.exception.ConstraintViolationException
EN

Stack Overflow用户
提问于 2017-07-13 10:50:52
回答 11查看 36.7K关注 0票数 29

我在实体类中定义了一个用于验证电子邮件的模式。在我的验证异常处理程序类中,我为ConstraintViolationException添加了处理程序。我的应用程序使用SpringBoot 1.4.5。

Profile.java

代码语言:javascript
复制
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "profile")
public class Profile extends AuditableEntity {

  private static final long serialVersionUID = 8744243251433626827L;

  @Column(name = "email", nullable = true, length = 250)
  @NotNull
  @Pattern(regexp = "^([^ @])+@([^ \\.@]+\\.)+([^ \\.@])+$")
  @Size(max = 250)
  private String email;
....
}

ValidationExceptionHandler.java

代码语言:javascript
复制
@ControllerAdvice
public class ValidationExceptionHandler extends ResponseEntityExceptionHandler {

  private MessageSource messageSource;

  @Autowired
  public ValidationExceptionHandler(MessageSource messageSource) {
    this.messageSource = messageSource;
  }

  @ExceptionHandler(ConstraintViolationException.class)
  public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex,
  WebRequest request) {
    List<String> errors = new ArrayList<String>();
    ....
    }
} 

当我运行我的代码并传递无效的电子邮件地址时,我得到以下异常。handleConstraintViolation中的代码永远不会执行。异常中返回的http状态是500,但我想返回400。你知道我该怎么做吗?

代码语言:javascript
复制
2017-07-12 22:15:07.078 ERROR 55627 --- [nio-9000-exec-2] o.h.c.s.u.c.UserProfileController        : Validation failed for classes [org.xxxx.common.service.user.domain.Profile] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='must match "^([^ @])+@([^ \.@]+\.)+([^ \.@])+$"', propertyPath=email, rootBeanClass=class org.xxxx.common.service.user.domain.Profile, messageTemplate='{javax.validation.constraints.Pattern.message}'}]

javax.validation.ConstraintViolationException: Validation failed for classes [org.xxxx.common.service.user.domain.Profile] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='must match "^([^ @])+@([^ \.@]+\.)+([^ \.@])+$"', propertyPath=email, rootBeanClass=class org.xxxx.common.service.user.domain.Profile, messageTemplate='{javax.validation.constraints.Pattern.message}'}]

at  org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:138)

at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:78)    
EN

回答 11

Stack Overflow用户

发布于 2017-09-21 20:47:42

你不能捕获ConstraintViolationException.class,因为它没有传播到代码的那一层,它被较低的层捕获,被包装在另一个类型下并重新抛出。因此,命中web层的异常不是ConstraintViolationException

在我的例子中,它是一个TransactionSystemException。我在JpaTransactionManager中使用了来自Spring的@Transactional注释。当事务中出现错误时,EntityManager抛出回滚异常,该异常由JpaTransactionManager转换为TransactionSystemException

所以你可以这样做:

代码语言:javascript
复制
@ExceptionHandler({ TransactionSystemException.class })
public ResponseEntity<RestResponseErrorMessage> handleConstraintViolation(Exception ex, WebRequest request) {
    Throwable cause = ((TransactionSystemException) ex).getRootCause();
    if (cause instanceof ConstraintViolationException) {
        Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) cause).getConstraintViolations();
        // do something here
    }
}
票数 34
EN

Stack Overflow用户

发布于 2018-12-10 23:10:08

我只想加点东西。我也在试着做同样的事情,验证实体。然后我意识到,如果你验证控制器的输入,Spring已经拥有了所有的东西。

代码语言:javascript
复制
@RequestMapping(value = "/profile", method = RequestMethod.POST)
public ProfileDto createProfile(@Valid ProfileDto profile){
...    
}

@Valid注解将触发带有javax.validation注解的验证。

假设您的配置文件用户名上有一个模式注释,其中regexp不允许使用空格。

Spring将构建一个状态为400 (错误请求)的响应和如下所示的主体:

代码语言:javascript
复制
{
    "timestamp": 1544453370570,
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "Pattern.ProfileDto.username",
                "Pattern.username",
                "Pattern.java.lang.String",
                "Pattern"
            ],
            "arguments": [
                {
                    "codes": [
                        "profileDto.username",
                        "username"
                    ],
                    "arguments": null,
                    "defaultMessage": "username",
                    "code": "username"
                },
                [],
                {
                    "defaultMessage": "^[A-Za-z0-9_\\-.]+$",
                    "arguments": null,
                    "codes": [
                        "^[A-Za-z0-9_\\-.]+$"
                    ]
                }
            ],
            "defaultMessage": "must match \"^[A-Za-z0-9_\\-.]+$\"",
            "objectName": "profileDto",
            "field": "username",
            "rejectedValue": "Wr Ong",
            "bindingFailure": false,
            "code": "Pattern"
        }
    ],
    "message": "Validation failed for object='profileDto'. Error count: 1",
    "path": "/profile"
}
票数 5
EN

Stack Overflow用户

发布于 2019-07-27 20:53:26

你不能捕获ConstraintViolationException.class,因为它没有传播到代码的那一层,它被较低的层捕获,被包装在另一个类型下并重新抛出。因此,命中web层的异常不是ConstraintViolationException。所以你可以这样做:

代码语言:javascript
复制
@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());
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45070642

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档