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

oyjwcjzk  于 2022-12-19  发布在  其他
关注(0)|答案(5)|浏览(234)

我使用JPA 2.0/Hibernate验证来验证我的模型,现在我遇到了两个字段的组合必须被验证的情况:

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

如果getValue1()getValue2()都是null,则模型为“无效”,否则有效。
如何使用JPA2.0/Hibernate执行这种验证?使用简单的@NotNull注解,两个getter都必须为非空才能通过验证。

ojsjcaue

ojsjcaue1#

对于多属性验证,您应该使用类级别约束。自定义约束:

类级约束

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

  • 街道名称有些标准,必须有长度限制
  • 邮政编码结构完全取决于国家
  • 城市通常可以与邮政编码相关联,并且可以进行一些错误检查(假设可以访问验证服务)
  • 由于这些相互依赖性,一个简单的属性级约束就能满足要求

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实现。通过访问对象示例,类级约束具有很大的灵活性,可以验证多个相关属性。注意,这里没有考虑排序,我们将在下一篇文章中回到它。
Maven组讨论了各种多属性支持方法:我们认为类级约束方法相对于其他涉及依赖关系的属性级方法提供了足够的简单性和灵活性。2欢迎您的反馈。

b0zn9rqh

b0zn9rqh2#

要正确使用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;
        }
    }
}
k3fezbri

k3fezbri3#

您可以像这样使用@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;
    }
}
b09cbbtk

b09cbbtk4#

当您希望继续使用Bean验证规范时,定制类级别验证器是一种可行的方法,例如这里的示例。
如果您愿意使用Hibernate验证器特性,可以使用 @ScriptAssert,它是从Validator-4.1.0.Final. Exceprt开始提供的,来自它的JavaDoc:
脚本表达式可以用任何脚本语言或表达式语言编写,在类路径中可以找到与JSR 223(“Scripting for the JavaTM Platform”)兼容的引擎。
示例:

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

63lcw9qa5#

编程语言: java
这是一个帮助了我的解决方案。
要求:

  1. UI上有一个包含对象列表的表,该表Map到具有fk关系的多个表/对象。
    1.现在验证是在多个fk之外,只有3列不能复制。我的意思是3列的组合不能复制。
    注意:由于我正在Java上开发自定义框架,所以没有使用HashCode或equals的选项。如果我使用数组索引迭代,这将增加时间复杂性,这是我不希望的。
    溶液:
    我准备了一个字符串,它是一个自定义字符串,包含FK1#ID FK2#ID FK3 Ex:字符串的形式为-〉1000L#3000L#1300L#
    这个字符串,我们将添加到一个集合使用add()的集合,这将返回假,如果重复出现.
    基于这个标志,我们可以抛出验证消息。
    这对我很有帮助。有些情况和限制可能会让DS帮不上忙。

相关问题