Bean Validation中一个未解决的问题是如何对集合(或可迭代)的元素应用约束。考虑一个包含电子邮件列表(主要、次要等)的用户实体。这可以建模如下
public class User { @NotEmpty private String name; @NotEmpty @Email private List<String> emailList; ... }
问题是,目前验证此类User的实例会抛出UnexpectedTypeException,因为没有内置的ConstraintValidator
不考虑语言限制,以下是一个不错的解决方案
public class User { @NotEmpty private String name; @NotEmpty private List<@Email String> emailList; ... }
当然,在当前版本的Java中这是不可能的。然而,有JSR 308,它允许上述示例,并有望成为Java 7的一部分。对于Bean Validation规范,等待此功能是有意义的(参见BVAL-202),但对于Hibernate Validator,我们应该有一个临时解决方案。这就是HV-296涉及的内容。HV-296讨论了多个解决方案。以下我想介绍两种解决方案
有效载荷解决方案
对于此解决方案,将引入一个自定义的有效载荷类。如果在此约束中指定此有效载荷,则约束将应用于集合的元素
public class User { @NotEmpty private String name; @NotEmpty @Email(payload=OnElements.class) private List< String> emailList; ... }
这似乎是最不侵入性的解决方案,并希望符合规范的规定:“有效载荷通常由验证客户端用于将一些元数据信息与给定的约束声明相关联。有效载荷通常是不可移植的”。
@ValidateEach注解解决方案
对于这个解决方案,我们将引入一个新的注解@ValidateEach(或者可能是@ApplyOn)。
@ValidateElements public @interface ValidateEach { Email[] value(); }
@ValidateElements是一个由验证引擎使用的标记注解,用于确定应用于集合的内置约束的注解。我们的用例现在看起来是这样的
public class User { @NotEmpty private String name; @NotEmpty @ValidateEach(@Email) private List< String> emailList; ... }
这个解决方案的问题在于,对于每个约束,我们都需要将相应的ValidateEach注解放置在其自己的包中。如果我们给每个注解一个独特的名称,比如ValidateEachEmail、ValidateEachSize等,就可以避免这种情况。或者我们可以有一个单一的ValidateEach注解和为每个内置约束设置属性
@ValidateElements public @interface ValidateEach { Email[] email() default{}; Size[] size() default {}; NotNull[] notNull() default {}; ... }
这将减少所需注解的数量,但会引入自定义约束的特殊情况。
通过阅读附加到HV-296的irc日志,您可以了解我们是如何到达这些解决方案的。如果您对这个话题有意见,请确保留下评论,在论坛上发帖或发送电子邮件到hibernate-dev。