Starting JPA 2.0, Bean Validation is supported by default, given that a provider of Bean Validation API should be on the classpath and bean constraint annotations are placed on the entity fields/getters.
According to the specification, the JPA provider uses pre-persist, pre-update, and pre-remove lifecycle entity events to invoke Bean validation API to perform validation on entities.
On validation failure ConstraintViolationException is thrown and the current transaction is mark for rollback.
Example
Additional Maven Dependencies
Other than JPA related dependencies, Bean Validation related necessary dependencies should also be present. In this example, we are going to use Hibernate Validation as the Bean Validation provider:
pom.xml<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.6.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.5</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.5</version>
</dependency>
The Entity
Using constraint annotations in our entity:
package com.logicbig.example;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
@Entity
public class User {
@Id
@GeneratedValue
private int id;
@NotNull
private String name;
@NotNull
@Pattern(regexp = "(\\d){3,3}-\\d{3,3}-\\d{4,4}",
message = "The phone number must match 111-111-1111 format")
private String phone;
.............
}
Persisting
We are purposely not going to populate our entity with valid values to see if validations are performed or not:
public class ExampleMain {
private static EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("example-unit");
public static void main(String[] args) {
try {
persistEntity();
} finally {
entityManagerFactory.close();
}
}
public static void persistEntity() {
User user = new User();
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit();
em.close();
}
}
Output
Exception in thread "main" javax.persistence.RollbackException: Error while committing the transaction
at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:77)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:71)
at com.logicbig.example.ExampleMain.persistEntity(ExampleMain.java:24)
at com.logicbig.example.ExampleMain.main(ExampleMain.java:13)
Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [com.logicbig.example.User] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='may not be null', propertyPath=name, rootBeanClass=class com.logicbig.example.User, messageTemplate='{javax.validation.constraints.NotNull.message}'}
ConstraintViolationImpl{interpolatedMessage='may not be null', propertyPath=phone, rootBeanClass=class com.logicbig.example.User, messageTemplate='{javax.validation.constraints.NotNull.message}'}
]
at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140)
at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80)
at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:205)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:82)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:589)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1437)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:493)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3207)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2413)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:156)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68)
... 2 more
Providing non-null values but setting invalid phone number pattern:
public class ExampleMain2 {
private static EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("example-unit");
public static void main(String[] args) {
try {
persistEntity();
} finally {
entityManagerFactory.close();
}
}
public static void persistEntity() {
User user = new User();
user.setName("Adam");
user.setPhone("111-111-111");
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit();
em.close();
}
}
Output
Exception in thread "main" javax.persistence.RollbackException: Error while committing the transaction
at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:77)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:71)
at com.logicbig.example.ExampleMain2.persistEntity(ExampleMain2.java:27)
at com.logicbig.example.ExampleMain2.main(ExampleMain2.java:13)
Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [com.logicbig.example.User] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='The phone number must match 111-111-1111 format', propertyPath=phone, rootBeanClass=class com.logicbig.example.User, messageTemplate='The phone number must match 111-111-1111 format'}
]
at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140)
at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80)
at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:205)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:82)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:589)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1437)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:493)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3207)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2413)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:156)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68)
... 2 more
Let's provide all valid values:
public class ExampleMain3 {
private static EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("example-unit");
public static void main(String[] args) {
try {
persistEntity();
loadEntity();
} finally {
entityManagerFactory.close();
}
}
public static void persistEntity() {
System.out.println("-- persisting --");
User user = new User();
user.setName("Adam");
user.setPhone("111-111-1111");
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit();
em.close();
}
private static void loadEntity() {
System.out.println("-- loading --");
EntityManager em = entityManagerFactory.createEntityManager();
List<User> entityAList = em.createQuery("Select t from User t")
.getResultList();
entityAList.forEach(System.out::println);
em.close();
}
} -- persisting -- -- loading -- User{id=1, name='Adam', phone='111-111-1111'}
Example ProjectDependencies and Technologies Used: - h2 1.4.196: H2 Database Engine.
- hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
Implements javax.persistence:javax.persistence-api version 2.1 - hibernate-validator 5.3.6.Final: Hibernate's Bean Validation (JSR-303) reference implementation.
- javax.el-api 2.2.5 Expression Language API 2.2
- javax.el 2.2.5 Expression Language 2.2 Implementation
- JDK 1.8
- Maven 3.3.9
|