I created the following Entity and embeddable:
@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;
...
}
I then tested the GlassFish4/Hibernate Validator 5.0.0 cascading validation functionality using a PersonCreater.xhtml JSF page and a PersonManager stateless session EJB as follows:
- Created Person.
- Added email with address of "adsj alkjdfa" to the Person email list.
- Added email with address of "#AV@#$R" to the Person email list.
- Persisted the object.
Unfortunately, I did not receive any errors and the Person object with the invalid email addresses was persisted to the database.
As a test, I changed the email property on my Person class from a list of email to a single email as shown below.
@Entity
public class Person implements Serializable {
@Valid
private Email email;
...
}
I then performed the following test.
- Created Person.
- Added email with address of "adsj alkjdfa" to the Person email list.
- Persisted the object.
As expected, I received a constraint violation stating that email did not match pattern and the Person was not persisted in the database.
As a final test, I added the following code to the addEmail method of my JSF CDI Bean in order to manually check the validations.
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);
When I entered an invalid email address, I received the following messages:
- Email constraint violations: [ConstraintViolationImpl{interpolatedMessage='Email Address must match pattern ...
- Person constraint violations: [ConstraintViolationImpl{interpolatedMessage='Email Address must match pattern...
- Person Email constraint violations: []
I expected the validator.validate(newEmail);
method call would throw the Pattern violation because I was validating the newEmail by itself.
However, I did not expect the validator.validate(person);
method call would find the Pattern violation, because I thought my problem was associated with my Entity and Embeddable classes.
These results tells me that I have configured my Entity and Embeddable classes correctly but that for some reason GlassFish is not automatically validating the email property when I create the Person using my JSF page or when I persist the Person using my EJB.
Do I need to configure GlassFish to automatically perform the cascading validation?
Finally, I don't know why the validator.validate(person);
method call found the Pattern violation and the validator.validateProperty(person, "email");
method call did not.
The following is my persistence-unit from my 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>
The following is the a more complete view of my Person Class:
@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>();
}
...
The following is a more complete version of my Email class:
@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;
...