2014-09-01 82 views
0

我创建了下面的实体和嵌入:为什么GlassFish 4不验证我的@Valid @ElementCollection字段的成员?

@Entity 
public class Person implements Serializable { 

    @NotNull 
    @Pattern(regexp = "([A-Z][a-z]*)*", message = "First Name must match pattern ([A-Z][a-z]*)*") 
    private String firstName; 

    @Valid 
    @ElementCollection 
    private List<Email> email; 
... 
} 

@Embeddable 
public class Email implements Serializable { 

    @NotNull (message = "Email Address may not be null") 
    @Pattern(regexp = "[-0-9a-zA-Z.+_][email protected][-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}", message = "Email Address must match pattern [-0-9a-zA-Z.+_][email protected][-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}") 
    private String address; 
... 
} 

然后我用PersonCreater.xhtml JSF页面和的PersonManager无状态会话EJB如下测试GlassFish4/Hibernate验证5.0.0级联验证功能:

  1. 已创建人员。
  2. 将“adsj alkjdfa”地址添加到Person邮件列表中。
  3. 将“#AV @#$ R”地址添加到个人邮件列表中。
  4. 坚持的对象。

不幸的是,我没有收到任何错误,并且具有无效电子邮件地址的Person对象被持久化到数据库。

作为测试,我将我的Person类中的电子邮件属性从电子邮件列表更改为单个电子邮件,如下所示。

@Entity 
public class Person implements Serializable { 

    @Valid 
    private Email email; 
... 
} 

然后我进行了以下测试。

  1. 创建人。
  2. 将“adsj alkjdfa”地址添加到Person邮件列表中。
  3. 坚持的对象。

正如预期的那样,我收到一个违反约束条件,说明电子邮件不符合模式,并且Person未保留在数据库中。

作为最终测试,我将以下代码添加到了我的JSF CDI Bean的addEmail方法中,以便手动检查验证。

Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); 

    Set<ConstraintViolation<Email>> emailCV = validator.validate(newEmail); 
    logger.debug("Email constraint violations: " + emailCV); 

    Set<ConstraintViolation<Person>> personCV = validator.validate(person); 
    logger.debug("Person constraint violations: " + personCV); 

    Set<ConstraintViolation<Person>> personEmailCV = validator.validateProperty(person, "email"); 
    logger.debug("Person Email constraint violations: " + personEmailCV); 

当我输入了一个无效的电子邮件地址,我收到以下消息:

  • 电子邮件约束违规:[{ConstraintViolationImpl = interpolatedMessage“电子邮件地址必须匹配模式...
  • 人约束违例:[ConstraintViolationImpl {interpolatedMessage ='电子邮件地址必须匹配模式...
  • 人员电子邮件约束违规:[]

我预计validator.validate(newEmail);方法调用会抛出模式违规,因为我自己验证了newEmail。

但是,我没想到validator.validate(person);方法调用会发现模式违规,因为我认为我的问题与我的实体和Embeddable类有关。

这些结果告诉我,我已经正确配置了我的Entity和Embeddable类,但由于某些原因,当我使用我的JSF页面创建Person或使用我的EJB持久化Person时,GlassFish不会自动验证电子邮件属性。

我是否需要配置GlassFish以自动执行级联验证?

最后,我不知道为什么validator.validate(person);方法调用发现模式违规和validator.validateProperty(person, "email");方法调用没有。

以下是我的persistence.xml中的持久性单元。

<persistence-unit name="ELISPU" transaction-type="JTA"> 

    <jta-data-source>jdbc/elis</jta-data-source> 
    <exclude-unlisted-classes>false</exclude-unlisted-classes> 

    <properties> 
     <property name="javax.persistence.schema-generation.database.action" value="create" /> 
    </properties> 

</persistence-unit> 

以下是我的Person类的更完整的视图:

@Entity 
@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
@NamedQueries({ 
     @NamedQuery(name = "Person.findAll", query = "SELECT p FROM Person p ORDER BY p.lastName, p.firstName"), 
     @NamedQuery(name = "Person.findByName", query = "SELECT p FROM Person p WHERE p.lastName = :lastName and p.firstName = :firstName and p.middleName = :middleName"), 
     @NamedQuery(name = "Person.findByShortName", query = "SELECT p FROM Person p WHERE p.shortName = :shortName") }) 
public class Person implements Serializable { 

    final static Logger logger = LoggerFactory.getLogger(Person.class.getName()); 

    static final long serialVersionUID = 1L; 

    @TableGenerator(name = "Person_Generator", table = "ID_Gen", pkColumnName = "GEN_NAME", valueColumnName = "GEN_VAL", initialValue = 0, allocationSize = 1) 
    @Id 
    @GeneratedValue(generator = "Person_Generator") 
    @XmlAttribute 
    private Long id; 
    @Version 
    @XmlAttribute 
    private Integer version; 
    private String prefixName; 
    @NotNull 
    @Pattern(regexp = "([A-Z][a-z]*)*", message = "First Name must match pattern ([A-Z][a-z]*)*") 
    private String firstName; 
    @Pattern(regexp = "([A-Z][a-z]*)*", message = "Middle Name must match pattern ([A-Z][a-z]*)*") 
    private String middleName; 
    @NotNull 
    @Pattern(regexp = "([A-Z][a-z]*)*", message = "Last Name must match pattern ([A-Z][a-z]*)*") 
    private String lastName; 
    private String suffixName; 
    @NotNull 
    @Pattern(regexp = "([A-Z][a-z]*)*", message = "Familiar Name must match pattern ([A-Z][a-z]*)*") 
    private String familiarName; 
    @NotNull 
    @Column(unique = true) 
    @Pattern(regexp = "[a-z0-9]*", message = "Short Name must match pattern [a-z0-9]*") 
    private String shortName; 
    private String description; 

    @Valid 
    @ElementCollection 
    private List<Email> email; 
    @ElementCollection 
    private List<Voice> voice; 
    @ElementCollection 
    private List<Address> addresses; 

    private ELISFile picture; 
    @XmlElementWrapper(name = "notes") 
    @XmlElement(name = "note") 
    @ElementCollection 
    private List<EntityNote> notes; 
    @XmlTransient 
    // @XmlElementWrapper(name="history") 
    // @XmlElement(name="event") 
    @NotNull 
    @ElementCollection 
    private List<EntityEvent> history; 

    public Person() { 

     logger.debug("Person created."); 

     email = new ArrayList<Email>(); 
     voice = new ArrayList<Voice>(); 
     addresses = new ArrayList<Address>(); 
     notes = new ArrayList<EntityNote>(); 
     history = new ArrayList<EntityEvent>(); 

    } 
... 

以下是我的电子邮件类的更完整的版本:

@Embeddable 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Email implements Serializable { 

    final static Logger logger = LoggerFactory.getLogger(Email.class.getName()); 

    static final long serialVersionUID = 1L; 

    @NotNull (message = "Email Type may not be null") 
    @Enumerated(EnumType.STRING) 
    private EmailType type; 
    @NotNull (message = "Email Address may not be null") 
    @Pattern(regexp = "[-0-9a-zA-Z.+_][email protected][-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}", message = "Email Address must match pattern [-0-9a-zA-Z.+_][email protected][-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}") 
    private String address; 
    private String description; 
    ... 

回答

1

问题不Glassfish,但您的模型以及JSF如何与Bean验证集成。正如您可能已经发现JSF不使用Validator#validte,而是使用Validator#validatePropery来验证单个属性的约束。前者验证完整对象图,而后者仅验证给定属性上指定的约束。 Validator#validateProperty不会执行级联验证。

JPA级别的验证(如果启用它)应该可以工作。在不同的JPA生命周期回调中会出现完整的实体验证。

+0

有趣:

我呼吁坚持在我的新的人,如下图所示工作过周围的bug通过手动进行验证。 JSF使用validateProperty部分验证Person实体,而validateProperty不尊重Person的email属性中的@Valid注释。这解释了为什么我的JSF客户端不报告约束违规。但是,我仍然不明白为什么当我坚持实体时,JPA级别没有验证电子邮件属性。我读过的所有内容都表明,默认情况下启用了bean验证。我是否必须将一些属性添加到我的persistence.xml文件以启用JPA级别bean验证?感谢您的帮助! – 2014-09-01 22:09:10

+0

默认情况下启用JPA生命周期事件的验证,但也可以通过某些属性进行控制。参见http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#d0e5193。为什么它不适用于你很难说,尤其是因为在你的例子中你没有显示你的JPA注释/配置。我建议打开调试级别日志记录来查看是否发生验证。如果您需要更多反馈,则需要更新您的问题并提供更多信息。 – Hardy 2014-09-02 09:31:27

+0

我将一个org.hibernate.validator记录器添加到了我的GlassFish 4服务器配置中,并观察验证器验证firstName属性是否为空并与该模式匹配的位置。如果我将@Size(min = 1)注释添加到电子邮件属性中,我会发现验证程序验证电子邮件属性上列表的大小,但不验证组成该列表的电子邮件对象的地址属性。我用一些配置信息更新了我的原始问题。 – 2014-09-03 04:10:07

0

对这个问题的回答有以下两部分,第1部分解释了为什么JSF没有收到验证错误,第2部分解释了为什么JPA没有收到验证错误。

第1部分

耐寒他回答说,JSF使用Hibernate验证的validateProperty方法来验证部分Person实体和validateProperty不接受对人的电子邮件属性@Valid注解。

第2部分

JPA/EclipseLink的GlassFish中4.0和4.1时的属性与@Valid @ElementCollection注释不级联到列表属性的元素。如果我从我的属性中删除了@ElementCollection,我可以看到级联验证按预期工作,但这打破了JPA,因此不是一个好的解决方法。我将此问题作为GLASSFISH-21184在GlassFish缺陷跟踪系统中记录下来。

Set<ConstraintViolation<Person>> personCV = validator.validate(person); 

if (personCV.size() > 0) 
    throw new ConstraintViolationException(personCV); 

em.persist(person); 
相关问题