Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How-to make Hibernate Validator stop validation on the first field violation?

I have a bean where I define multiple validation annotations for each field e.g.

@NotEmpty
@Pattern(regexp="(\\-?\\d)+")
@Min(value=1)
String myField;

I have encountered two one problem that I cannot resolve in any easy way.

  1. Validation order of the specified annotations for each field is random i.e. do not happen in the order the annotations are defined. I believe @GroupSequence will not help since it only defines group validation sequence, not annotation sequence. As @Tom correctly commented, violations are reported as Set which means there is no 1:1 mapping between execution order of annotations and reported violations.
  2. I want to invalidate only one rule for each field i.e. if it does not match pattern do not try to check if the value is >= 1. Currently if is set myField to "abc" it will report both @Pattern and @Min violations. Setting failFast property of a validator to true does not help because I'm using the same validator instance to validate all the fields in my bean, and it will stop validating other fields as soon as the first violation for the whole bean is encountered.

Edit. I tried to implement custom composite constraint with @ReportAsSingleViolation. The problem is that it will report the same message for all violations involved in composition. This is not what I need.

Suggestions, please?

like image 233
mmierins Avatar asked Nov 19 '14 13:11

mmierins


1 Answers

It should be possible to create the behavior you want to achieve using a combination of validation groups and a fully defined validation group sequence

Quoting from JSR-349 (aka. BeanValidation 1.1) spec, paragraph 4.4.2

Processing a group is defined in Section 4.6 ; if one of the groups processed in the sequence generates one or more constraint violations, the groups following in the sequence must not be processed. This ensures that a set of constraint is evaluated only if another set of constraint is valid.
highlighting by me

This should be relatively easy, going by the Group Sequence Example

@GroupSequence({YourClass.class, Second.class, Third.class})
public class YourClass {
    @NotNull
    @Pattern(regexp="(\\-?\\d)+", groups=Second.class)
    @Min(value=1, groups=Third.class)
    String myField;
}

with Second and Third defined as simple marker-interfaces should do the trick. Be aware that this does not validate all fields until the first constraint violation, but just until the first overall violation.

This means some of your fields may become invalid after fixing other fields' violations.

If all else fails you can still reimplement the behavioral components, your Validation Provider. You should be able to provide your own implementation of the ConstraintValidatorContext by using a validation.xml

Be aware you'd have to break the contract of ConstraintValidatorContext.ConstraintViolationBuilder:

To create the ConstraintViolation, one must call either one of the addConstraintViolation() methods available in one of the interfaces of the fluent API.

If another method is called after addConstraintViolation() on ConstraintViolationBuilder or any of its associated objects an IllegalStateException is raised.

like image 80
Vogel612 Avatar answered Sep 30 '22 15:09

Vogel612