Spring DataIntegrityViolationException

1. Übersicht

In diesem Artikel wird die Spring org.springframework.dao.DataIntegrityViolationException erläutert. Dies ist eine generische Datenausnahme, die normalerweise vom Spring Exception Translation-Mechanismus ausgelöst wird, wenn Persistenzausnahmen niedrigerer Ebene behandelt werden. In diesem Artikel werden die häufigsten Ursachen dieser Ausnahme sowie die jeweilige Lösung erläutert.

2. DataIntegrityViolationException und Spring Exception Translation

Der Spring-Ausnahmemechanismus für Ausnahmen kann transparent auf alle mit @Repository kommentierten Beans angewendet werden, indem im Kontext eine Post-Prozessor-Bean für Exception-Translation-Beans definiert wird:

Oder in Java:

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

Der Ausnahmeübersetzungsmechanismus ist standardmäßig auch für die ältere Persistenzvorlage aktiviert, die in Spring verfügbar ist - HibernateTemplate, JpaTemplate usw.

3. Wo wird die DataIntegrityViolationException ausgelöst ?

3.1. DataIntegrityViolationException mit Ruhezustand

Wenn Spring mit Hibernate konfiguriert ist, wird die Ausnahme in der von Spring - SessionFactoryUtils - convertHibernateAccessException bereitgestellten Ausnahmeübersetzungsebene ausgelöst .

Es gibt drei mögliche Ausnahmen im Ruhezustand , die dazu führen können, dass die DataIntegrityViolationException ausgelöst wird:

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

3.2. DataIntegrityViolationException Mit JPA

Wenn Spring mit JPA als Persistenzanbieter konfiguriert ist, wird die DataIntegrityViolationException ähnlich wie im Ruhezustand in der Ausnahmeübersetzungsschicht ausgelöst - nämlich in EntityManagerFactoryUtils - convertJpaAccessExceptionIfPossible .

Es gibt eine einzige Ausnahme , die JPA ein auslösen können DataIntegrityViolationException geworfen werden - die javax.persistence.EntityExistsException .

4. Ursache: org.hibernate.exception.ConstraintViolationException

Dies ist bei weitem die häufigste Ursache für das Auslösen der DataIntegrityViolationException. Die Hibernate ConstraintViolationException zeigt an, dass der Vorgang eine Datenbankintegritätsbeschränkung verletzt hat.

Betrachten Sie das folgende Beispiel - für One to One - Mapping durch eine explizite Fremdschlüsselspalte zwischen einem Elternteil und Kind Entitäten - die folgenden Vorgänge fehlschlagen sollten:

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

Die übergeordnete Entität hat einen Fremdschlüssel für die untergeordnete Entität. Wenn Sie also das untergeordnete Element löschen, wird die Fremdschlüsseleinschränkung für die übergeordnete Entität aufgehoben. Dies führt zu einer ConstraintViolationException, die von Spring in die DataIntegrityViolationException eingeschlossen wird :

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

Um dies zu lösen, sollte der Elternteil zuerst gelöscht werden:

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

Dies ist eine der häufigsten Ursachen für die DataIntegrityViolationException. Im Ruhezustand ist dies darauf zurückzuführen, dass eine Entität weiterhin ein Problem hat. Entweder hat die Entität eine Null-Eigenschaft, die mit einer Nicht-Null- Einschränkung definiert ist , oder eine Zuordnung der Entität kann auf eine nicht gespeicherte, vorübergehende Instanz verweisen .

Zum Beispiel hat die folgenden Unternehmen eine nicht-null Namen Eigenschaft -

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

Wenn der folgende Test versucht, die Entität mit einem Nullwert für den Namen beizubehalten :

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

Eine Datenbankintegrationsbeschränkung wird verletzt, und daher wird die DataIntegrityViolationException ausgelöst:

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. Ursache: org.hibernate.exception.DataException

Eine DataException im Ruhezustand zeigt eine ungültige SQL-Anweisung an - in diesem bestimmten Kontext stimmte etwas mit der Anweisung oder den Daten nicht. Wenn Sie beispielsweise eine oder eine Foo- Entität von zuvor verwenden, wird diese Ausnahme durch Folgendes ausgelöst:

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

Die eigentliche Ausnahme für das Behalten des Objekts mit einem langen Namenswert ist:

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 diesem Beispiel besteht die Lösung darin, die maximale Länge des Namens anzugeben:

@Column(nullable = false, length = 4096)

7. Ursache: javax.persistence.EntityExistsException

Ähnlich wie im Ruhezustand wird die JPA-Ausnahme EntityExistsException auch von der Spring Exception Translation in eine DataIntegrityViolationException eingeschlossen . Der einzige Unterschied besteht darin, dass JPA selbst bereits ein hohes Niveau aufweist, was diese JPA-Ausnahme zur einzigen potenziellen Ursache für Verstöße gegen die Datenintegrität macht.

8. Potenziell DataIntegrityViolationException

In einigen Fällen, in denen die DataIntegrityViolationException erwartet werden kann, kann eine andere Ausnahme ausgelöst werden. Ein solcher Fall liegt vor, wenn im Klassenpfad ein JSR-303-Validator wie der Hibernate-Validator 4 oder 5 vorhanden ist.

Wenn in diesem Fall die folgende Entität mit einem Nullwert für name beibehalten wird, schlägt sie nicht mehr mit einer durch die Persistenzschicht ausgelösten Verletzung der Datenintegrität fehl :

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

Dies liegt daran, dass die Ausführung nicht in die Persistenzschicht gelangt - sie schlägt zuvor mit einer javax.validation.ConstraintViolationException fehl :

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. Schlussfolgerungen

Am Ende dieses Artikels sollten wir eine übersichtliche Übersicht über die verschiedenen Ursachen und Probleme haben, die im Frühjahr zu einer DataIntegrityViolationException führen können , sowie einen guten Überblick darüber, wie all diese Probleme behoben werden können.

Die Implementierung aller Ausnahmebeispiele finden Sie im Github-Projekt - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.