Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between ConstraintViolationException and MethodArgumentNotValidException

When I am validating bean using @valid annotation in javax.validations, for some objects I am getting ConstraintViolationException and for some I am getting MethodArgumentNotValidException.

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

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

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

I am finding it bit difficult to understand as it's 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.

like image 897
JOHND Avatar asked Jul 12 '19 16:07

JOHND


People also ask

What is the difference between @valid and @validated?

The @Valid annotation ensures the validation of the whole object. Importantly, it performs the validation of the whole object graph. However, this creates issues for scenarios needing only partial validation. On the other hand, we can use @Validated for group validation, including the above partial validation.

What does @validated do?

The @Validated annotation is a class-level annotation that we can use to tell Spring to validate parameters that are passed into a method of the annotated class.

What is the use of @valid in Spring boot?

The @Valid annotation will tell spring to go and validate the data passed into the controller by checking to see that the integer numberBetweenOneAndTen is between 1 and 10 inclusive because of those min and max annotations.

What is @validated annotation in Java?

The @Valid annotation is a key feature of Bean Validation, as it allows to validate object graphs with a single call to the validator. To make use of it all fields that should be recursively checked should be annotated with @Valid .


2 Answers

for a simple understanding, if validation happens at controller/service layer by using @Valid annotation, it generates MethodArgumentNotValidException, you can add handler for this and return the response accordingly, this class is part of spring framework and validation is performed by 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;
}

and if you do not validate using @Valid annotation, and exception is raised by hibernate at jpa layer it generates ConstraintViolationException, this exception is part of Javax bean validation framework, and raised at the time of performing 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;
    }
like image 168
Hashir Labs Avatar answered Sep 19 '22 07:09

Hashir Labs


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.

like image 33
Mohamed Sweelam Avatar answered Sep 18 '22 07:09

Mohamed Sweelam