What is the difference between ConstraintViolationException and MethodArgumentNotValidException

16k Views Asked by At

When I am validating bean using @Valid annotation in javax.validation, for some objects I am getting ConstraintViolationException and for some I am getting a MethodArgumentNotValidException.

I understand that, if I validate anything in @ResponseBody in the controller , it throws a MethodArgumentNotValidException.

But for some custom validations(eg. @MyCustomValidation) at the class level it is throwing ConstraintViolationException even if it is being validated in @ResponseValidation.

And for some other custom validation for a different REST endpoint, it throws MethodArgumentNotValidException.

I am finding it a bit difficult to understand its behavior.

@PostMapping(path = "/someEndPoint")    
@Validated(OnASave.class)
public ResponseEntity<ClassA> saveObjA(@Valid @RequestBody ClassA objA)

Result - throws MethodArgumentNotValidException

@PostMapping(path = "/someOtherEndPoint")   
@Validated(OnBSave.class)
public ResponseEntity<ClassB> saveObjB(@Valid @RequestBody ClassB objB)

Result - throws ConstraintViolationException

Both ClassA and ClassB has custom validations.

3

There are 3 best solutions below

2
Mohamed Sweelam On

When you use @Valid, you are applying validation which is defined by you on your model class fields, while there are different types of validations, you can choose like @NotNull, @Max, @Min and so on, you will get the matching type.

In general, all of these are parallel to MethodArgumentNotValidException which will be thrown in all cases.

From official document

Exception to be thrown when validation on an argument annotated with @Valid fails.

ConstraintViolationException is thrown by hibernate entity manager when some constrain violated, so this means you violated some fields in some entity you are using.

0
Christopher Yang On

Take a look at this article. These exceptions are thrown under different situations when you mix spring's validation framework with bean validation.

0
Hashir Labs On

For a simple understanding, if validation happens at the controller/service layer by using the @Valid annotation, it generates MethodArgumentNotValidException. You can add a handler for this and return the response accordingly. This class is part of the Spring framework, and validation is performed by the Spring framework. See sample below:

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Response> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
    
        logger.info("Invalid arguments found : " + ex.getMessage());
        // Get the error messages for invalid fields
        List<FieldError> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(fieldError -> new FieldError(fieldError.getField(), fieldError.getDefaultMessage()))
                .collect(Collectors.toList());
    
        String message = messageSource.getMessage("invalid.data.message", null, LocaleContextHolder.getLocale());
        Response response = new Response(false, message)
                .setErrors(errors);
        ResponseEntity<Response> responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
        return responseEntity;
    }

Instead, if you do not validate using the @Valid annotation, an exception is raised by Hibernate at the JPA layer and generates ConstraintViolationException. This exception is part of the Javax bean validation framework and raised at the time of performing the persistence operation (before the actual SQL execution). See sample below:

    @ExceptionHandler(ConstraintViolationException.class)
        public ResponseEntity<Response> handleConstraintViolationException(ConstraintViolationException ex) {
            List<FieldError> errors = ex.getConstraintViolations()
                    .stream()
                    .map(constraintViolation -> {
                        return new FieldError(constraintViolation.getRootBeanClass().getName() + " " + constraintViolation.getPropertyPath(), constraintViolation.getMessage());
                    })
                    .collect(Collectors.toList());
    
            String message = messageSource.getMessage("invalid.data.message", null, LocaleContextHolder.getLocale());
            Response response = new Response(false, message)
                    .setErrors(errors);
            ResponseEntity<Response> responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
            return responseEntity;
        }