Spring DataIntegrityViolationException

1. Panoramica

In questo articolo, discuteremo di Spring org.springframework.dao.DataIntegrityViolationException : si tratta di un'eccezione di dati generica tipicamente generata dal meccanismo di conversione dell'eccezione Spring quando si tratta di eccezioni di persistenza di livello inferiore. L'articolo discuterà le cause più comuni di questa eccezione insieme alla soluzione per ciascuna di esse.

2. DataIntegrityViolationException e Spring Exception Translation

Il meccanismo di traduzione delle eccezioni Spring può essere applicato in modo trasparente a tutti i bean annotati con @Repository , definendo un bean post processore del bean di traduzione delle eccezioni nel contesto:

O in Java:

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

Il meccanismo di traduzione delle eccezioni è abilitato per impostazione predefinita anche sul vecchio modello di persistenza disponibile in Spring: HibernateTemplate, JpaTemplate, ecc.

3. Dove viene generata DataIntegrityViolationException

3.1. DataIntegrityViolationException con Hibernate

Quando Spring è configurato con Hibernate, l' eccezione viene generata nel livello di traduzione delle eccezioni fornito da Spring - SessionFactoryUtils - convertHibernateAccessException .

Esistono tre possibili eccezioni di Hibernate che possono causare la generazione di DataIntegrityViolationException :

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

3.2. DataIntegrityViolationException con JPA

Quando Spring è configurato con JPA come provider di persistenza, viene generata DataIntegrityViolationException , simile a Hibernate, nel livello di traduzione delle eccezioni, ovvero in EntityManagerFactoryUtils - convertJpaAccessExceptionIfPossible .

Esiste una singola eccezione JPA che può attivare la generazione di un'eccezione DataIntegrityViolationException : javax.persistence.EntityExistsException .

4. Causa: org.hibernate.exception.ConstraintViolationException

Questa è di gran lunga la causa più comune della generazione di DataIntegrityViolationException : Hibernate ConstraintViolationException indica che l'operazione ha violato un vincolo di integrità del database.

Considera il seguente esempio: per il mapping uno a uno tramite una colonna di chiave esterna esplicita tra un'entità padre e un'entità figlio , le seguenti operazioni dovrebbero non riuscire:

@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); }

L' entità Parent ha una chiave esterna per l' entità Child , quindi l'eliminazione del figlio interromperà il vincolo di chiave esterna su Parent, il che si traduce in un'eccezione ConstraintViolationException , avvolta da Spring in 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

Per risolvere questo problema, è necessario eliminare prima il genitore :

@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

Questa è una delle cause più comuni dell'eccezione DataIntegrityViolationException : in Hibernate, questo si riduce a un'entità che persiste con un problema. L'entità ha una proprietà null definita con un vincolo non null oppure un'associazione dell'entità può fare riferimento a un'istanza transitoria non salvata .

Ad esempio, la seguente entità ha una proprietà di nome non nullo :

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

Se il test seguente tenta di rendere persistente l'entità con un valore nullo per nome :

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

Un vincolo di integrità del database viene violato e quindi viene generata l' eccezione 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

Un'eccezione Hibernate DataException indica un'istruzione SQL non valida: qualcosa non andava nell'istruzione o nei dati, in quel particolare contesto. Ad esempio, utilizzando o l' entità Foo di prima, quanto segue attiverà questa eccezione:

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

L'eccezione effettiva per la persistenza dell'oggetto con un valore di nome lungo è:

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)

In questo particolare esempio, la soluzione è specificare la lunghezza massima del nome:

@Column(nullable = false, length = 4096)

7. Causa: javax.persistence.EntityExistsException

Analogamente a Hibernate, anche l' eccezione JPA EntityExistsException verrà incorporata dalla traduzione dell'eccezione Spring in un'eccezione DataIntegrityViolationException . L'unica differenza è che lo stesso JPA è già di alto livello, il che rende questa eccezione JPA l'unica potenziale causa di violazioni dell'integrità dei dati.

8. Potenzialmente DataIntegrityViolationException

In alcuni casi in cui è prevista l'eccezione DataIntegrityViolationException , potrebbe essere generata un'altra eccezione: uno di questi casi è se un validatore JSR-303, come hibernate-validator 4 o 5 esiste sul classpath.

In tal caso, se la seguente entità viene mantenuta con un valore nullo per nome , non fallirà più con una violazione dell'integrità dei dati innescata dal livello di persistenza:

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

Questo perché l'esecuzione non raggiungerà il livello di persistenza, ma prima fallirà con un'eccezione 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. Conclusioni

Alla fine di questo articolo, dovremmo avere una mappa chiara per esplorare la varietà di cause e problemi che possono portare a un'eccezione DataIntegrityViolationException in primavera, oltre a una buona conoscenza di come risolvere tutti questi problemi.

L'implementazione di tutti gli esempi di eccezioni può essere trovata nel progetto github: questo è un progetto basato su Eclipse, quindi dovrebbe essere facile da importare ed eseguire così com'è.