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:
... // getter and setter following
@ValidateFieldIfFlag
public class Model {
@Id
@GeneratedValue
private int id;
private boolean flag;
private String field;
... // getter and setter following
}
@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)
public @interface ValidateFieldIfFlagValidator
String message() default "my error message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@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 {};
}
@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();
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();
}
}
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();
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;
}
}
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”.