Spring DataIntegrityViolationException

Spring DataIntegrityViolationException

1. Vue d'ensemble

Dans cet article, nous discuterons des Springorg.springframework.dao.DataIntegrityViolationException - il s'agit d'une exception de données générique généralement lancée par le mécanisme de traduction d'exceptions Spring lors du traitement des exceptions de persistance de niveau inférieur. L'article discutera des causes les plus courantes de cette exception avec la solution pour chacune d'elles.

Lectures complémentaires:

Prise en charge de Spring Data Java 8

Guide rapide et pratique sur la prise en charge de Java 8 dans Spring Data.

Read more

Annotations de données de printemps

Découvrez les annotations les plus importantes dont nous avons besoin pour gérer la persistance à l'aide du projet Spring Data.

Read more

Utilisation de relations dans REST Data Spring

Guide pratique sur l'utilisation des relations d'entité dans Spring Data REST.

Read more

2. DataIntegrityViolationException et traduction des exceptions Spring

Le mécanisme de traduction d'exception Spring peut être appliqué de manière transparente à tous les beans annotés avec@Repository - en définissant un bean de post-processeur de bean de traduction d'exception dans le contexte:

Ou en Java:

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

Le mécanisme de traduction des exceptions est également activé par défaut sur l'ancien modèle de persistance disponible dans Spring - HibernateTemplate, JpaTemplate, etc.

3. Où estDataIntegrityViolationException jeté

3.1. DataIntegrityViolationException avec Hibernate

Lorsque Spring est configuré avec Hibernate, leexception est jeté dans la couche de traduction d'exception fournie par Spring -SessionFactoryUtils – convertHibernateAccessException.

Il existe trois exceptions Hibernate possibles qui peuvent provoquer la levée desDataIntegrityViolationException:

  • org.hibernate.exception.ConstraintViolationException

  • org.hibernate.PropertyValueException

  • org.hibernate.exception.DataException

3.2. DataIntegrityViolationException avec JPA

Lorsque Spring est configuré avec JPA comme fournisseur de persistance, leDataIntegrityViolationException est jeté, similaire à Hibernate, dans la couche de traduction d'exception - à savoir dansEntityManagerFactoryUtils – convertJpaAccessExceptionIfPossible.

Il existe une seule exception JPA qui peut déclencher la levée d'unDataIntegrityViolationException - lesjavax.persistence.EntityExistsException.

4. Cause:org.hibernate.exception.ConstraintViolationException

C'est de loin la cause la plus fréquente du rejet deDataIntegrityViolationException - le HibernateConstraintViolationException indique que l'opération a violé une contrainte d'intégrité de la base de données.

Prenons l'exemple suivant - pour un mappage un à un via une colonne de clé étrangère explicite entre les entitésParent etChild - les opérations suivantes doivent échouer:

@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 a une clé étrangère pour l'entitéChild - donc la suppression de l'enfant briserait la contrainte de clé étrangère sur le parent - ce qui se traduit par unConstraintViolationException - enveloppé par Spring dans leDataIntegrityViolationException:

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

Pour résoudre ce problème, lesParent doivent d'abord être supprimés:

@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. Cause:org.hibernate.PropertyValueException

C'est l'une des causes les plus courantes desDataIntegrityViolationException - dans Hibernate, cela se résumera à une entité persistante avec un problème. Soit l'entité a une propriété nulle qui est définie avec unnot-null constraint, soit une association de l'entité peut référencer ununsaved, transient instance.

Par exemple, l'entité suivante a une propriéténame non nulle -

@Entity
public class Foo {
   ...

   @Column(nullable = false)
   private String name;

   ...
}

Si le test suivant tente de conserver l'entité avec une valeur nulle pourname:

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

Une contrainte d'intégrité de base de données est violée, et donc leDataIntegrityViolationException est levé:

org.springframework.dao.DataIntegrityViolationException:
not-null property references a null or transient value:
org.example.spring.persistence.model.Foo.name;
nested exception is org.hibernate.PropertyValueException:
not-null property references a null or transient value:
org.example.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.example.spring.persistence.model.Foo.name
    at o.h.e.i.Nullability.checkNullability(Nullability.java:103)
...

6. Cause:org.hibernate.exception.DataException

Un HibernateDataException indique une instruction SQL non valide - quelque chose n'allait pas avec l'instruction ou les données, dans ce contexte particulier. Par exemple, en utilisant l'entité ouFoo antérieure, ce qui suit déclencherait cette exception:

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

L'exception réelle pour la persistance de l'objet avec une longue valeurname est:

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)

Dans cet exemple particulier, la solution consiste à spécifier la longueur maximale du nom:

@Column(nullable = false, length = 4096)

7. Cause:javax.persistence.EntityExistsException

De la même manière que Hibernate, l'exception JPAEntityExistsException sera également encapsulée par la traduction d'exception Spring dans unDataIntegrityViolationException. La seule différence est que JPA elle-même est déjà d'un niveau élevé, ce qui fait de cette exception JPA la seule cause potentielle de violations de l'intégrité des données.

8. PotentiellementDataIntegrityViolationException

Dans certains cas où lesDataIntegrityViolationException peuvent être attendus, une autre exception peut être levée - un tel cas est si un validateur JSR-303, tel quehibernate-validator 4 ou 5 existe sur le chemin de classe.

Dans ce cas, si l'entité suivante est conservée avec une valeur nulle pourname, elle serano longer fail with a data integrity violation déclenchée par la couche de persistance:

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

    ...
}

C'est parce que l'exécution n'atteindra pas la couche de persistance - elle échouera avant cela avec unjavax.validation.ConstraintViolationException:

javax.validation.ConstraintViolationException:
Validation failed for classes [org.example.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.example.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. Conclusions

À la fin de cet article, nous devrions avoir une carte claire pour naviguer dans la variété des causes et des problèmes qui peuvent conduire à unDataIntegrityViolationException au printemps, ainsi qu'une bonne compréhension de la façon de résoudre tous ces problèmes.

L'implémentation de tous les exemples d'exceptions peut être trouvée dansthe github project - il s'agit d'un projet basé sur Eclipse, il devrait donc être facile à importer et à exécuter tel quel.