Validate field / property constraint before class level constraint

Description

Validate field / property constraints before class level constraint is validated.

Currently class level constraints are validated before single field constraints by default.
This requires us to repeat some validation in the class level constraint like checking for null values.

Improvement

A bean is never valid if any single field is invalid. In other words, it is a precondition that all fields must be valid so that a class level constraint can be valid.

Following example would throw a NPE in the `isValid` method, since `@NotNull` on property `value` was not evaluated before `@FooValidator`

Related SO questions

Environment

None

Activity

Show:
Gunnar Morling
August 23, 2018, 6:15 AM

Hi , thanks for chiming in! I don't quite understand what you suggest. Can you perhaps give an example and show a bean with some valid/invalid constraints and describe what should happen exactly? Thanks!

Rene de Waele
August 23, 2018, 7:39 PM
Edited

Hi Gunnar, sure.

Actually, the example from Marco Janc in this issue would work as well, but let me give another example:

If we inspect the following Foo instance (shown as json):

Then the @AssertTrue validation will fail because the value in bar is not 42. However, if we pass the following instance:

then it would be nice if the validator returns that the @NotNull constraint was violated. Instead, a ValidationException is thrown because the @AssertTrue validation resulted in a NullPointerException.

My suggestion is to swallow those exceptions if there are other constraint violations. In this case that would mean that the framework would return that a single constraint was violated.

Only in case when there are NO constraint violations but one or more constraint validations yield a ValidationException does the framework throw out the ValidationException. Here's an example of that:

In this case the @AssertTrue validation will also result in a ValidationException but there are no other constraint violations. In that case the framework should not swallow the ValidationException but throw it in the same way as it does currently.

This solution works no matter the validation order and "fail-fast" is not required. If you check the ValidatorImpl I've attached you can see I made 2 small changes in methods validateConstraintsForSingleDefaultGroupElement(...) and validateMetaConstraints(...). While looping over the constraints any ValidationException is caught. The ValidationException is only thrown if there are no other violations.

Hope this clarified things. I've tested my solution in a large project containing hundreds of annotated beans and it seems to do the trick. Let me know what you think.

Gunnar Morling
August 28, 2018, 10:59 AM

Hi , thanks for providing that example; I think I understand what you have in mind, I'm still having doubts though whether that'd be the best thing to do. Specifically, your example would be better addressed by using the @Valid annotation like so:

IIUC, this would result in the behavior you're after, in particular the NPE would be avoided, as the @AssertTrue constraint on Bar would only be validated if Foo#bar is not null. Or is this just addressing a specific sub-set of the cases you have in mind?

Rene de Waele
August 28, 2018, 6:11 PM

Hi Gunnar, unfortunately that’s not going to work.

There isn’t a constraint on Bar that its value always needs to be 42! There’s a constraint on Foo that its Bar property always has a value of 42. That’s a big difference.

Gunnar Morling
August 29, 2018, 6:57 AM
Edited

Ok, I see. The current way of achieving this would be to leverage group conversions:

That way, the @AssertTrue constraint would only be validated when validating a Bar instance in the course of cascaded validation from Foo. Would that help to address your use case?

Assignee

Unassigned

Reporter

Marco Janc

Labels

None

Feedback Requested

None

Feedback Requested By

None

backPortable

None

Suitable for new contributors

None

Pull Request

None

backportDecision

None

backportReEvaluate

None

Components

Priority

Major
Configure