mmierins
mmierins

Reputation: 3774

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?

Upvotes: 8

Views: 7689

Answers (4)

HungNM2
HungNM2

Reputation: 3425

see guide: failFast mode

Validator validator = Validation.byProvider( HibernateValidator.class )
        .configure()
        .failFast( true ) // stop at first 
        .buildValidatorFactory()
        .getValidator();

Upvotes: 0

Ondra Žižka
Ondra Žižka

Reputation: 46796

So far you have to use the @GroupSequence solution described in the other answer. However, the Hibernate team is looking for a solution with the coming features of JDK, and even working with OpenJDK developers: BVAL-248

Upvotes: 0

Vogel612
Vogel612

Reputation: 5647

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.

Upvotes: 11

Zoran Regvart
Zoran Regvart

Reputation: 4690

You could compose your constraints. But with that you would get only one error for any of the constraints specified, not sure if thats acceptable in your use case.

Upvotes: 1

Related Questions