Sometimes it is necessary to validate one property or multiple properties of a class at one time or in relation to each other, but you want to add a constraint violation to the specific property. The solution will described in this post
You have your model:
@ValidateFieldIfFlag public class Model { @Id @GeneratedValue private int id; private boolean flag; private String field; ... // getter and setter following }
As you see the class uses a custom validator ValidateFieldIfFlag to validate a Model instance.
So you have the annotation:
@Constraint(validatedBy = {ValidateFieldIfFlagValidator.class}) @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) @Documented public @interface ValidateFieldIfFlagValidator { String message() default "my error message"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
And your validator:
public class ValidateFieldIfFlagValidator implements ConstraintValidator<ValidateFieldIfFlagValidator, Model> { public boolean isValid(Model model, ConstraintValidatorContext context) { if (!model.isFlag()) return true; //if flag is set, field should be nonEmpty return model.getField() != null && !model.getField().isEmpty(); } }
The validator will validate the field
to be non empty, if the flag
is set. Unfortunately you will receive a ConstraintViolationException
for the whole instance and not only for the field. To change this, you have to modify the validator:
public class ValidateFieldIfFlagValidator implements ConstraintValidator<ValidateFieldIfFlagValidator, Model> { public boolean isValid(Model model, ConstraintValidatorContext context) { if (!model.isFlag()) return true; //if flag is set, field should be nonEmpty if (model.getField() != null && !model.getField().isEmpty()) return true; ConstraintValidatorContext.ConstraintViolationBuilder constraintViolationBuilder = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()); constraintViolationBuilder.addPropertyNode("field").addConstraintViolation(); context.disableDefaultConstraintViolation(); return false; } }
This suppress the default, class matched ConstraintViolationException, and creates a ConstraintViolationException matching the property “field”.