Cross field validation with Hibernate Validator (JSR 303)

前端 未结 15 1772
渐次进展
渐次进展 2020-11-22 02:37

Is there an implementation of (or third-party implementation for) cross field validation in Hibernate Validator 4.x? If not, what is the cleanest way to implement a cross fi

15条回答
  •  囚心锁ツ
    2020-11-22 03:05

    I'm surprised this isn't available out of the box. Anyway, here is a possible solution.

    I've created a class level validator, not the field level as described in the original question.

    Here is the annotation code:

    package com.moa.podium.util.constraints;
    
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.*;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Constraint(validatedBy = MatchesValidator.class)
    @Documented
    public @interface Matches {
    
      String message() default "{com.moa.podium.util.constraints.matches}";
    
      Class[] groups() default {};
    
      Class[] payload() default {};
    
      String field();
    
      String verifyField();
    }
    

    And the validator itself:

    package com.moa.podium.util.constraints;
    
    import org.mvel2.MVEL;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    public class MatchesValidator implements ConstraintValidator {
    
      private String field;
      private String verifyField;
    
    
      public void initialize(Matches constraintAnnotation) {
        this.field = constraintAnnotation.field();
        this.verifyField = constraintAnnotation.verifyField();
      }
    
      public boolean isValid(Object value, ConstraintValidatorContext context) {
        Object fieldObj = MVEL.getProperty(field, value);
        Object verifyFieldObj = MVEL.getProperty(verifyField, value);
    
        boolean neitherSet = (fieldObj == null) && (verifyFieldObj == null);
    
        if (neitherSet) {
          return true;
        }
    
        boolean matches = (fieldObj != null) && fieldObj.equals(verifyFieldObj);
    
        if (!matches) {
          context.disableDefaultConstraintViolation();
          context.buildConstraintViolationWithTemplate("message")
              .addNode(verifyField)
              .addConstraintViolation();
        }
    
        return matches;
      }
    }
    

    Note that I've used MVEL to inspect the properties of the object being validated. This could be replaced with the standard reflection APIs or if it is a specific class you are validating, the accessor methods themselves.

    The @Matches annotation can then be used used on a bean as follows:

    @Matches(field="pass", verifyField="passRepeat")
    public class AccountCreateForm {
    
      @Size(min=6, max=50)
      private String pass;
      private String passRepeat;
    
      ...
    }
    

    As a disclaimer, I wrote this in the last 5 minutes, so I probably haven't ironed out all the bugs yet. I'll update the answer if anything goes wrong.

提交回复
热议问题