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

Posted

技术标签:

【中文标题】如何组合验证两个或多个字段?【英文标题】:How can I validate two or more fields in combination? 【发布时间】:2011-02-16 10:17:20 【问题描述】:

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

public class MyModel 
    public Integer getValue1() 
        //...
    
    public String getValue2() 
        //...
    

如果getValue1()getValue2() 都是null,则模型无效,否则有效。

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

【问题讨论】:

Cross field validation with Hibernate Validator (JSR 303) 的可能重复项 【参考方案1】:

您可以像这样使用@AssertTrue 验证:

public class MyModel 
    
    // values

    @AssertTrue(message = "Values are invalid")
    private String isValid() 
      return value1 != null || value2 != null;
    

【讨论】:

感谢您的解决方案。最初我想使用@ScriptAssert,但有些 bean 有许多需要验证的规则,所以这更直观:每个规则一个验证方法。【参考方案2】:

对于多个属性验证,您应该使用类级别的约束。从 Bean Validation Sneak Peek part II: custom constraints:

类级约束

你们中的一些人表达了担忧 关于申请的能力 跨越多个的约束 属性,或表达约束 这取决于几个属性。 经典的例子是地址 验证。地址错综复杂 规则:

街道名称有点标准,当然必须有长度限制 邮政编码结构完全取决于国家/地区 通常可以将城市与邮政编码相关联,并且可以进行一些错误检查 完成(前提是验证 服务可用) 由于这些相互依赖关系,一个简单的属性级别约束 符合要求

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

它提供了在执行之前强制应用一组约束的能力 其他一组约束通过 使用组和组序列。 该主题将在 下一篇博文 它允许定义类级别的约束

类级约束是常规的 约束(注释/ 实施二重奏),适用于 类而不是属性。说 不同的是,类级别的约束 接收对象实例(而不是 比属性值)在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("@AddressAnnotation only applies to Address objects");
        
        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;
        
        // ...
    

高级地址验证规则 已被排除在地址之外 对象并由 MultiCountryAddressValidator。经过 访问对象实例,类 水平约束有很多 灵活性,可以验证多个 相关属性。注意 排序被排除在等式之外 在这里,我们将在 下一篇文章。

专家组讨论了各种 多属性支持 做法:我们认为班级水平 约束方法提供了 足够的简单性和灵活性 与其他物业水平相比 涉及依赖的方法。 欢迎您的反馈。

【讨论】:

接口ConstraintValidator和注解@Constraint在例子中已经倒置了。并且是 valid() 需要 2 个参数。 TYPERUNTIME 应分别替换为 ElementType.TYPERetentionPolicy.RUNTIME @mark.monteiro 您可以使用静态导入:import static java.lang.annotation.ElementType.*;import static java.lang.annotation.RetentionPolicy.*; 我已经重写了示例以使用 Bean Validation。看看here。 注释的参数不在规范范围内,因为必须有一条消息、组和有效负载,就像 Cassio 在这个答案下提到的那样。【参考方案3】:

编程语言:Java

这是一个对我有帮助的解决方案。

要求:

    在 UI 上有一个包含对象列表的表,该表正在映射到具有 fk 关系的多个表/对象。

    现在验证来自多个 fks,只有 3 列不能重复。我的意思是3的组合不能重复。

注意:当我在 Java 上开发自定义框架时,没有使用 HashCode 或 equals 的选项。如果我将使用数组索引迭代会增加我不想要的时间复杂度。

解决方案:

我准备了一个 String ,它是一个自定义 String ,其中包含 FK1 的 ID#FK2 的 ID#FK3 的 ID 例如:字符串将形成 -> 1000L#3000L#1300L#

这个字符串,我们将使用 set 的 add() 添加到一个 set 中,如果出现重复则返回 false。

基于这个标志,我们可以抛出验证消息。

这对我有帮助。 DS 可能无法提供一些方案和限制。

【讨论】:

【参考方案4】:

如果您想保持 Bean 验证规范,例如 here,则可以使用自定义类级别验证器。

如果您乐于使用 Hibernate Validator 功能,您可以使用 @ScriptAssert,它从 Validator-4.1.0.Final 开始提供。摘自其 JavaDoc:

脚本表达式可以用任何脚本或表达式编写 语言,JSR 223(“JavaTM 平台脚本”) 可以在类路径中找到兼容的引擎。

例子:

@ScriptAssert(lang = "javascript", script = "_this.value1 != null || _this != value2)")
public class MyBean 
  private String value1;
  private String value2;

【讨论】:

是的,Java 6 包含 Rhino(JavaScript 引擎),因此您可以使用 JavaScript 作为表达式语言而无需添加额外的依赖项。 Here 是如何使用 Hibernate Validator 5.1.1.Final 进行此类验证的示例【参考方案5】:

为了与Bean Validation 正常工作,Pascal Thivent 的answer 中提供的示例可以重写如下:

@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

public class Country 

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

    // Getters and setters

@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 ;

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;
        
    

【讨论】:

如何在 WebSphere RESTful 项目中为 CDI bean 引导或调用自定义验证器?我已经写了所有,但自定义约束不起作用或调用 我遇到了类似的验证,但我的 isoA2Code 存储在 DB Country 表中。从这里拨打数据库电话是个好主意吗?另外,我想在验证后链接它们,因为Address belongs_to 一个Country 并且我希望address 条目具有country 表的外键。我如何将国家/地区与地址联系起来? 请注意,当您在错误的对象上设置类型验证注解时,Bean Validation 框架将抛出​​异常。例如,如果您在 Country 对象中设置 @ValidAddress 注释,您将获得 No validator could be found for constraint 'com.example.validation.ValidAddress' validating type 'com.example.Country' 异常。

以上是关于如何组合验证两个或多个字段?的主要内容,如果未能解决你的问题,请参考以下文章

我如何需要一个字段或另一个字段或(其他两个字段之一)但不是全部?

如何组合两个或多个参数化的 Monos Webflux 最佳实践

如何在c#中动态组合两个或多个DataTables

如何在 Jenkins“执行 Windows 批处理命令”中组合两个或多个命令

如何使用组合框在记录 ms 访问中搜索多个字段?

如何让mysql按照两个或多个字段排序