如何组合验证两个或多个字段?

Dan*_*ski 74 java jpa bean-validation jpa-2.0

我正在使用JPA 2.0/Hibernate验证来验证我的模型.我现在有一种情况,必须验证两个字段的组合:

public class MyModel {
    public Integer getValue1() {
        //...
    }
    public String getValue2() {
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

该模型是无效的,如果这两个getValue1()getValue2()null和有效的,否则.

如何使用JPA 2.0/Hibernate执行此类验证?使用简单的@NotNull注释,两个getter都必须为非null才能通过验证.

Pas*_*ent 87

对于多个属性验证,您应该使用类级别约束.从 Bean Validation Sneak Peek第二部分:自定义约束:

类级约束

你们中的一些人已经表达了对应用跨越多个属性的约束或表达依赖于几个属性的约束的能力的担忧.经典的例子是地址验证.地址有复杂的规则:

  • 街道名称有点标准,必须有一个长度限制
  • 邮政编码结构完全取决于国家
  • 城市通常可以与邮政编码相关联,并且可以进行一些错误检查(前提是可以访问验证服务)
  • 由于这些相互依赖性,一个简单的属性级别约束确实适合该法案

Bean Validation规范提供的解决方案有两个方面:

  • 它提供了通过使用组和组序列强制在一组其他约束之前应用一组约束的能力.这个主题将在下一篇博客文章中介绍
  • 它允许定义类级别约束

类级别约束是常规约束(注释/实现二重奏),它适用于类而不是属性.换句话说,类级约束接收对象实例(而不是属性值)isValid.

@AddressAnnotation 
public class Address {
    @NotNull @Max(50) private String street1;
    @Max(50) private String street2;
    @Max(10) @NotNull private String zipCode;
    @Max(20) @NotNull String city;
    @NotNull private Country country;

    ...
}

@Constraint(validatedBy = MultiCountryAddressValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AddressAnnotation {
    String message() default "{error.address}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

public class MultiCountryAddressValidator implements ConstraintValidator<AddressAnnotation, Address> {
    public void initialize(AddressAnnotation constraintAnnotation) {
    // initialize the zipcode/city/country correlation service
    }

    /**
     * Validate zipcode and city depending on the country
     */
    public boolean isValid(Address object, ConstraintValidatorContext context) {
        if (!(object instanceof Address)) {
            throw new IllegalArgumentException("@Address only applies to Address");
        }
        Address address = (Address) object;
        Country country = address.getCountry();
        if (country.getISO2() == "FR") {
            // check address.getZipCode() structure for France (5 numbers)
            // check zipcode and city correlation (calling an external service?)
            return isValid;
        } else if (country.getISO2() == "GR") {
            // check address.getZipCode() structure for Greece
            // no zipcode / city correlation available at the moment
            return isValid;
        }
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

高级地址验证规则已被遗漏在地址对象之外并由其实现 MultiCountryAddressValidator.通过访问对象实例,类级别约束具有很大的灵活性,可以验证多个相关属性.请注意,这里的排序不在等式中,我们将在下一篇文章中回到它.

专家组讨论了各种多属性支持方法:与其他涉及依赖关系的属性级方法相比,我们认为类级别约束方法提供了足够的简单性和灵活性.欢迎您的反馈.

  • 在示例中,接口ConstraintValidator和注释@Constraint已被反转.并且有效()需要2个参数. (17认同)
  • “TYPE”和“RUNTIME”应分别替换为“ElementType.TYPE”和“RetentionPolicy.RUNTIME”。 (2认同)
  • @ mark.monteiro你可以使用静态导入:`import static java.lang.annotation.ElementType.*;`和`import static java.lang.annotation.RetentionPolicy.*;` (2认同)
  • 我已经重写了示例以使用 Bean 验证。看看[这里](/sf/answers/3102859791/)。 (2认同)

J.F*_*.F. 34

@javax.validation.constraints.AssertTrue您可以像这样使用验证:

public class MyModel {
    
    private String value1;
    private String value2;

    @AssertTrue(message = "Values are invalid")
    private boolean isValid() {
        return value1 != null || value2 != null;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这比编写自定义“ConstraintValidator”要简单得多,而且需要的代码也更少。我认为对于这个简单的用例来说,这应该优于公认的解决方案。 (3认同)

cas*_*lin 27

要使用Bean Validation正常工作,Pascal Thivent的答案中提供的示例可以重写如下:

@ValidAddress
public class Address {

    @NotNull
    @Size(max = 50)
    private String street1;

    @Size(max = 50)
    private String street2;

    @NotNull
    @Size(max = 10)
    private String zipCode;

    @NotNull
    @Size(max = 20)
    private String city;

    @Valid
    @NotNull
    private Country country;

    // Getters and setters
}
Run Code Online (Sandbox Code Playgroud)
public class Country {

    @NotNull
    @Size(min = 2, max = 2)
    private String iso2;

    // Getters and setters
}
Run Code Online (Sandbox Code Playgroud)
@Documented
@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = { MultiCountryAddressValidator.class })
public @interface ValidAddress {

    String message() default "{com.example.validation.ValidAddress.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
Run Code Online (Sandbox Code Playgroud)
public class MultiCountryAddressValidator 
       implements ConstraintValidator<ValidAddress, Address> {

    public void initialize(ValidAddress constraintAnnotation) {

    }

    @Override
    public boolean isValid(Address address, 
                           ConstraintValidatorContext constraintValidatorContext) {

        Country country = address.getCountry();
        if (country == null || country.getIso2() == null || address.getZipCode() == null) {
            return true;
        }

        switch (country.getIso2()) {
            case "FR":
                return // Check if address.getZipCode() is valid for France
            case "GR":
                return // Check if address.getZipCode() is valid for Greece
            default:
                return true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Har*_*rdy 8

当您想要继续使用Bean Validation规范时,可以使用类级别验证器.如果您乐意使用Hibernate Validator功能,可以使用Validator-4.1.0.Final中提供的@ScriptAssert.

  • [here](http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-class-level-constraints)是如何使用Hibernate Validator创建一个这样的验证的示例5.1.1.Final (3认同)