Jakarta ee 为什么GlassFish 4不验证我的@Valid@ElementCollection字段的成员?
我创建了以下实体并可嵌入:Jakarta ee 为什么GlassFish 4不验证我的@Valid@ElementCollection字段的成员?,jakarta-ee,bean-validation,glassfish-4,hibernate-validator,Jakarta Ee,Bean Validation,Glassfish 4,Hibernate Validator,我创建了以下实体并可嵌入: @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 p
@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.+_]+@[-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}", message = "Email Address must match pattern [-0-9a-zA-Z.+_]+@[-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}")
private String address;
...
}
然后我进行了以下测试
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);
以下是我的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>();
}
...
问题不在于Glassfish,而在于您的模型以及JSF如何与Bean验证集成。您可能已经发现,JSF并没有使用
Validator#validte
,而是使用Validator#validateProperty
来验证单个属性上的约束。前者验证完整对象图,而后者仅验证给定属性上指定的约束。级联验证不由验证程序#validateProperty
执行
不过,JPA级别的验证(如果您启用它)应该可以工作。在不同的JPA生命周期回调上会发生完整的实体验证。这个问题的答案有以下两部分,其中第1部分解释了JSF没有捕获验证错误的原因,第2部分解释了JPA没有捕获验证错误的原因 第一部分 正如Hardy在回答中所说,JSF使用Hibernate Validator的validateProperty方法部分验证Person实体,validateProperty不支持Person的email属性上的@Valid注释 第二部分 GlassFish 4.0和4.1中的JPA/EclipseLink在使用@Valid@ElementCollection注释list属性时不会级联到该属性的元素中。如果我从我的属性中删除@ElementCollection,我可以看到级联验证按预期工作,但这破坏了JPA,因此不是一个好的解决方法。我在GlassFish bug跟踪系统中记录了这个问题,名为GlassFish-21184 我通过在调用persist之前手动执行验证来解决该错误,如下所示:
Set<ConstraintViolation<Person>> personCV = validator.validate(person);
if (personCV.size() > 0)
throw new ConstraintViolationException(personCV);
em.persist(person);
Set personCV=validator.validate(个人);
如果(personCV.size()>0)
抛出新的ConstraintViolationException(personCV);
em.person;
很有趣。JSF使用validateProperty部分验证Person实体,validateProperty不支持Person的email属性上的@Valid注释。这解释了为什么我的JSF客户端不报告约束冲突。但是,我仍然不理解为什么JPA级别在我持久化实体时不验证电子邮件属性。我读到的所有内容都表明默认情况下启用了bean验证。我是否必须向persistence.xml文件添加一些属性才能启用JPA级别的bean验证?谢谢你的帮助!默认情况下启用JPA生命周期事件验证,但也可以由某些属性控制。看见很难说为什么它对您不起作用,特别是因为在您的示例中,您没有显示JPA注释/配置。我建议打开调试级日志记录以查看是否发生验证。如果你想要更多反馈,你需要用更多信息更新你的问题。我在GlassFish 4服务器配置中添加了一个org.hibernate.validator记录器,并观察到验证器正在验证firstName属性是否为null并与模式匹配。如果我在email属性中添加@Size(min=1)注释,我会发现验证器会在email属性上验证列表的大小,但不会验证组成列表的email对象的address属性。我用一些配置信息更新了我的原始问题。
@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.+_]+@[-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}", message = "Email Address must match pattern [-0-9a-zA-Z.+_]+@[-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}")
private String address;
private String description;
...
Set<ConstraintViolation<Person>> personCV = validator.validate(person);
if (personCV.size() > 0)
throw new ConstraintViolationException(personCV);
em.persist(person);