如何组合验证两个或多个字段?
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 个参数。TYPE
和 RUNTIME
应分别替换为 ElementType.TYPE
和 RetentionPolicy.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 最佳实践