Spring DataIntegrityViolationException

1. Información general

En este artículo, analizaremos la excepción Spring org.springframework.dao.DataIntegrityViolationException : esta es una excepción de datos genéricos que normalmente genera el mecanismo de traducción de excepciones Spring cuando se trata de excepciones de persistencia de nivel inferior. El artículo discutirá las causas más comunes de esta excepción junto con la solución para cada una.

2. DataIntegrityViolationException y Spring Exception Translation

El mecanismo de traducción de excepción de Spring se puede aplicar de forma transparente a todos los beans anotados con @Repository , definiendo un bean de postprocesador de bean de traducción de excepción en el contexto:

O en Java:

@Configuration public class PersistenceHibernateConfig{ @Bean public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){ return new PersistenceExceptionTranslationPostProcessor(); } }

El mecanismo de traducción de excepciones también está habilitado de forma predeterminada en la plantilla de persistencia anterior disponible en Spring: HibernateTemplate, JpaTemplate, etc.

3. Cuando se DataIntegrityViolationException Thrown

3.1. DataIntegrityViolationException con Hibernate

Cuando Spring se configura con Hibernate, la excepción se lanza en la capa de traducción de excepciones proporcionada por Spring - SessionFactoryUtils - convertHibernateAccessException .

Hay tres posibles excepciones de Hibernate que pueden provocar que se lance DataIntegrityViolationException :

  • org.hibernate.exception.ConstraintViolationException
  • org.hibernate.PropertyValueException
  • org.hibernate.exception.DataException

3.2. DataIntegrityViolationException con JPA

Cuando Spring se configura con JPA como su proveedor de persistencia, se lanza DataIntegrityViolationException , similar a Hibernate, en la capa de traducción de excepciones, es decir, en EntityManagerFactoryUtils - convertJpaAccessExceptionIfPossible .

Existe una única excepción JPA que puede desencadenar el lanzamiento de una DataIntegrityViolationException : la javax.persistence.EntityExistsException .

4. Causa: org.hibernate.exception.ConstraintViolationException

Esta es, con mucho, la causa más común de que se arroje DataIntegrityViolationException : la Hibernate ConstraintViolationException indica que la operación ha violado una restricción de integridad de la base de datos.

Consideremos el siguiente ejemplo - para el mapeo uno a uno a través de una columna de clave externa explícita entre una de Padres y Niños entidades - las siguientes operaciones deben fallar:

@Test(expected = DataIntegrityViolationException.class) public void whenChildIsDeletedWhileParentStillHasForeignKeyToIt_thenDataException() { Child childEntity = new Child(); childService.create(childEntity); Parent parentEntity = new Parent(childEntity); service.create(parentEntity); childService.delete(childEntity); }

El Padre entidad tiene una clave externa a la del niño entidad - de modo de eliminar el niño se rompería la restricción de clave externa de la Sociedad dominante - que se traduce en una ConstraintViolationException - envuelto en la primavera en el DataIntegrityViolationException :

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement at o.s.orm.h.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:138) Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement

Para resolver esto, primero se debe eliminar el Padre :

@Test public void whenChildIsDeletedAfterTheParent_thenNoExceptions() { Child childEntity = new Child(); childService.create(childEntity); Parent parentEntity = new Parent(childEntity); service.create(parentEntity); service.delete(parentEntity); childService.delete(childEntity); }

5. Causa: org.hibernate.PropertyValueException

Esta es una de las causas más comunes de la excepción DataIntegrityViolationException : en Hibernate, esto se reducirá a que una entidad persista con un problema. O la entidad tiene una propiedad nula que se define con una restricción no nula , o una asociación de la entidad puede hacer referencia a una instancia transitoria no guardada .

Por ejemplo, la siguiente entidad tiene una propiedad de nombre no nula :

@Entity public class Foo { ... @Column(nullable = false) private String name; ... }

Si la siguiente prueba intenta persistir la entidad con un valor nulo para el nombre :

@Test(expected = DataIntegrityViolationException.class) public void whenInvalidEntityIsCreated_thenDataException() { fooService.create(new Foo()); }

Se infringe una restricción de la integridad de la base de datos, por lo que se lanza la excepción DataIntegrityViolationException :

org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value: org.baeldung.spring.persistence.model.Foo.name; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: org.baeldung.spring.persistence.model.Foo.name at o.s.orm.h.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:160) ... Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: org.baeldung.spring.persistence.model.Foo.name at o.h.e.i.Nullability.checkNullability(Nullability.java:103) ...

6. Causa: org.hibernate.exception.DataException

Una Hibernate DataException indica una declaración SQL no válida: algo estaba mal con la declaración o los datos, en ese contexto particular. Por ejemplo, al usar la entidad Foo de antes, lo siguiente desencadenaría esta excepción:

@Test(expected = DataIntegrityViolationException.class) public final void whenEntityWithLongNameIsCreated_thenDataException() { service.create(new Foo(randomAlphabetic(2048))); }

La excepción real para conservar el objeto con un valor de nombre largo es:

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement at o.s.o.h.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:143) ... Caused by: org.hibernate.exception.DataException: could not execute statement at o.h.e.i.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:71)

En este ejemplo en particular, la solución es especificar la longitud máxima del nombre:

@Column(nullable = false, length = 4096)

7. Causa: javax.persistence.EntityExistsException

De manera similar a Hibernate, la excepción EntityExistsException JPA también será envuelta por Spring Exception Translation en una DataIntegrityViolationException . La única diferencia es que JPA en sí ya es de alto nivel, lo que hace que esta excepción de JPA sea la única causa potencial de violaciones de la integridad de los datos.

8. Potencialmente DataIntegrityViolationException

En algunos casos en los que se puede esperar la excepción DataIntegrityViolationException, se puede generar otra excepción; uno de esos casos es si existe un validador JSR-303, como hibernate-validator 4 o 5 en la ruta de clases.

En ese caso, si la siguiente entidad persiste con un valor nulo para el nombre , ya no fallará con una violación de la integridad de los datos activada por la capa de persistencia:

@Entity public class Foo { ... @Column(nullable = false) @NotNull private String name; ... }

Esto se debe a que la ejecución no llegará a la capa de persistencia; fallará antes de eso con una javax.validation.ConstraintViolationException :

javax.validation.ConstraintViolationException: Validation failed for classes [org.baeldung.spring.persistence.model.Foo] during persist time for groups [javax.validation.groups.Default, ] List of constraint violations:[ ConstraintViolationImpl{ interpolatedMessage="may not be null", propertyPath=name, rootBeanClass=class org.baeldung.spring.persistence.model.Foo, messageTemplate="{javax.validation.constraints.NotNull.message}"} ] at o.h.c.b.BeanValidationEventListener.validate(BeanValidationEventListener.java:159) at o.h.c.b.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:94)

9. Conclusiones

Al final de este artículo, deberíamos tener un mapa claro para navegar por la variedad de causas y problemas que pueden conducir a una excepción DataIntegrityViolationException en Spring, así como una buena comprensión de cómo solucionar todos estos problemas.

La implementación de todos los ejemplos de excepciones se puede encontrar en el proyecto github; este es un proyecto basado en Eclipse, por lo que debería ser fácil de importar y ejecutar como está.