Close

Bean Validation integration in JPA

[Updated: Feb 6, 2018, Created: Jan 29, 2018]

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 Project

Dependencies 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

JPA + Bean Validation Example Select All Download
  • jpa-bean-validation-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • User.java
          • resources
            • META-INF

    See Also