Java 基元集合的Hibernate验证

Java 基元集合的Hibernate验证,java,collections,bean-validation,hibernate-validator,Java,Collections,Bean Validation,Hibernate Validator,我希望能够做到以下几点: @Email public List<String> getEmailAddresses() { return this.emailAddresses; } @电子邮件 公共列表getEmailAddresses() { 返回此电子邮件地址; } 换句话说,我希望列表中的每个项目都被验证为电子邮件地址。当然,这样注释集合是不可接受的 有办法做到这一点吗?JSR-303和Hibernate Validator都没有现成的约束可以验证集合的每个元素 解

我希望能够做到以下几点:

@Email
public List<String> getEmailAddresses()
{
   return this.emailAddresses;
}
@电子邮件
公共列表getEmailAddresses()
{
返回此电子邮件地址;
}
换句话说,我希望列表中的每个项目都被验证为电子邮件地址。当然,这样注释集合是不可接受的


有办法做到这一点吗?

JSR-303和Hibernate Validator都没有现成的约束可以验证集合的每个元素

解决此问题的一个可能的解决方案是创建自定义
@ValidCollection
约束和相应的验证器实现
ValidCollectionValidator

要验证集合的每个元素,我们需要一个
Validator
内部
ValidCollectionValidator
的实例;为了获得这样的实例,我们需要定制
ConstraintValidatorFactory
的实现

看看你是否喜欢下面的解决方案

简单地说

  • 复制粘贴所有这些java类(并导入相关类)
  • 在类路径上添加验证api、hibenate验证程序、slf4j-log4j12和testng JAR
  • 运行测试用例
有效收集

    public @interface ValidCollection {

    Class<?> elementType();

    /* Specify constraints when collection element type is NOT constrained 
     * validator.getConstraintsForClass(elementType).isBeanConstrained(); */
    Class<?>[] constraints() default {};

    boolean allViolationMessages() default true;

    String message() default "{ValidCollection.message}";

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

    Class<? extends Payload>[] payload() default {};

}
    public class CollectionElementBean {

    /* add more properties on-demand */
    private Object notNull;
    private String notBlank;
    private String email;

    protected CollectionElementBean() {
    }

    @NotNull
    public Object getNotNull() { return notNull; }
    public void setNotNull(Object notNull) { this.notNull = notNull; }

    @NotBlank
    public String getNotBlank() { return notBlank; }
    public void setNotBlank(String notBlank) { this.notBlank = notBlank; }

    @Email
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

}
CollectionElementBean

    public @interface ValidCollection {

    Class<?> elementType();

    /* Specify constraints when collection element type is NOT constrained 
     * validator.getConstraintsForClass(elementType).isBeanConstrained(); */
    Class<?>[] constraints() default {};

    boolean allViolationMessages() default true;

    String message() default "{ValidCollection.message}";

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

    Class<? extends Payload>[] payload() default {};

}
    public class CollectionElementBean {

    /* add more properties on-demand */
    private Object notNull;
    private String notBlank;
    private String email;

    protected CollectionElementBean() {
    }

    @NotNull
    public Object getNotNull() { return notNull; }
    public void setNotNull(Object notNull) { this.notNull = notNull; }

    @NotBlank
    public String getNotBlank() { return notBlank; }
    public void setNotBlank(String notBlank) { this.notBlank = notBlank; }

    @Email
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

}
ConstraintValidatorFactoryImpl

public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory {

    private ValidatorContext validatorContext;

    public ConstraintValidatorFactoryImpl(ValidatorContext nativeValidator) {
        this.validatorContext = nativeValidator;
    }

    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
        T instance = null;

        try {
            instance = key.newInstance();
        } catch (Exception e) { 
            // could not instantiate class
            e.printStackTrace();
        }

        if(ValidatorContextAwareConstraintValidator.class.isAssignableFrom(key)) {
            ValidatorContextAwareConstraintValidator validator = (ValidatorContextAwareConstraintValidator) instance;
            validator.setValidatorContext(validatorContext);
        }

        return instance;
    }

}

注意:-当bean有约束时,不要指定
@ValidCollection
约束的
约束
属性。当bean没有约束时,
constraints
属性是必需的。

感谢becomputer06的精彩回答。 但我认为应在ValidCollection定义中添加以下注释:

@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidCollectionValidator.class)
我仍然不知道如何处理基本类型包装器集合和约束注释,如@Size、@Min、@Max等,因为值不能通过becomputer06传递


当然,我可以为应用程序中的所有情况创建自定义约束注释,但无论如何,我必须将这些注释的属性添加到CollectionElementBean中。这似乎是一个很糟糕的解决方案。

由于Java注释本身的限制,不可能编写像
@EachElement
这样的通用包装器注释来包装任何约束注释。但是,您可以编写一个通用约束验证器类,该类将每个元素的实际验证委托给现有约束验证器。您必须为每个约束编写一个包装器注释,但只有一个验证器

我已经在(Maven Central中提供)中实现了这种方法。例如:

@EachSize(min = 5, max = 255)
List<String> values;

编辑:已更新库的当前版本。

JSR-303能够扩展内置约束的目标类型:请参阅


您可以实现一个
ConstraintValidator
,它执行与给定答案相同的操作,委托给原语验证器。然后,您可以保留您的模型定义,并在
列表中应用
@Email

我没有足够高的声誉对原始答案进行评论,但也许值得注意的是,这个问题正处于最终发布阶段,将在发布时解决这个问题!然而,它至少需要Java8

唯一的区别是验证注释将放在类型声明中

//@Email
public List<@Email String> getEmailAddresses()
{
   return this.emailAddresses;
}
/@电子邮件
公共列表getEmailAddresses()
{
返回此电子邮件地址;
}
请让我知道你认为我最好把这些信息放在哪里,以供其他正在寻找的人使用。谢谢


附:有关更多信息,可以使用非常简单的解决方法。您可以改为验证包装simple value属性的类集合。为此,您需要在集合上使用
@Valid
注释

示例:

public class EmailAddress {

  @Email
  String email;

  public EmailAddress(String email){
    this.email = email;
  }
}

public class Foo {

  /* Validation that works */
  @Valid
  List<EmailAddress> getEmailAddresses(){
    return this.emails.stream().map(EmailAddress::new).collect(toList());
  }

}
公共类电子邮件地址{
@电子邮件
字符串电子邮件;
公共电子邮件地址(字符串电子邮件){
this.email=电子邮件;
}
}
公开课Foo{
/*有效的验证*/
@有效的
列出getEmailAddresses(){
返回此.emails.stream().map(EmailAddress::new).collect(toList());
}
}

多好的答案啊!我会尽快处理这个问题。谢谢你,becomputer06!非常详细和详细的回答!这看起来太棒了,伙计,可惜打不出来:(。那会让一切变得更美好elegant@Stef看看当前版本。@JakubJirutka我对我的自定义约束做了完全相同的事情,但是我从
CommonEachValidator
初始化中得到一个异常,说我正在使用
Awesome.class
,本身没有验证器吗?它仅使用
@模式
约束,而不使用任何约束else@JakubJirutka是的,对于自定义验证器,我收到了相同的消息。例如,有一个验证集合的伟大框架的示例将非常好。这样一来,人们就可以直接复制……除非我遗漏了你的文档。我想我不是。提前谢谢。@JakubJirutka好的,现在对我有效(我的错误),但我没有收到集合中所有失败条目的错误消息…所以我有点回到原点…换句话说,如果我有一组1个有效值和2个无效值,它会在第一个无效值之后停止验证吗,或者它会验证所有的错误,并返回整个集合的错误,这就是我正在寻找的。提前感谢…这个例子将非常棒…因为我非常确定这是一个常见的用例…无论验证在哪里失败,都可以验证所有内容。有趣的方法。但是,我找不到任何对支持新的
TYPE\u USE
注释的验证程序包的引用。我只发现这篇文章提到Hibernate Validator 5.2可能支持它:这是正确的答案,前提是您在方法中添加了
@Valid
(而不是注释的
/@Email
)链接到文档?以下是
02:16:37,581  INFO main validation.ValidCollectionTest:66 - {ValidCollection.message}
02:16:38,303  INFO main validation.ValidCollectionTest:66 - may not be null
02:16:39,092  INFO main validation.ValidCollectionTest:66 - not a well-formed email address

02:17:46,460  INFO main validation.ValidCollectionTest:81 - may not be empty
02:17:47,064  INFO main validation.ValidCollectionTest:81 - {ValidCollection.message}
@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidCollectionValidator.class)
@EachSize(min = 5, max = 255)
List<String> values;
// common boilerplate
@Documented
@Retention(RUNTIME)
@Target({METHOD, FIELD, ANNOTATION_TYPE})
// this is important!
@EachConstraint(validateAs = Awesome.class)
@Constraint(validatedBy = CommonEachValidator.class)
public @interface EachAwesome {

    // copy&paste all attributes from Awesome annotation here
    String message() default "";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String someAttribute();
}
//@Email
public List<@Email String> getEmailAddresses()
{
   return this.emailAddresses;
}
public class EmailAddress {

  @Email
  String email;

  public EmailAddress(String email){
    this.email = email;
  }
}

public class Foo {

  /* Validation that works */
  @Valid
  List<EmailAddress> getEmailAddresses(){
    return this.emails.stream().map(EmailAddress::new).collect(toList());
  }

}