Spring DataIntegrityViolationException

Spring DataIntegrityViolationException

1. 概要

この記事では、Springorg.springframework.dao.DataIntegrityViolationExceptionについて説明します。これは、低レベルの永続性例外を処理するときに、Spring例外変換メカニズムによって通常スローされる一般的なデータ例外です。 この記事では、この例外の最も一般的な原因とその解決策について説明します。

参考文献:

Spring Data Java 8のサポート

Spring DataでのJava 8サポートの迅速かつ実用的なガイド。

春のデータ注釈

Spring Dataプロジェクトを使用して永続性を処理するために必要な最も重要なアノテーションについて学びます。

Spring Data RESTでのリレーションシップの使用

Spring Data RESTでエンティティ関係を操作するための実用的なガイド。

2. DataIntegrityViolationExceptionおよびSpring例外変換

Spring例外変換メカニズムは、@Repositoryでアノテーションが付けられたすべてのBeanに透過的に適用できます–コンテキストで例外変換BeanポストプロセッサBeanを定義することにより、次のようになります。

またはJavaの場合:

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

例外変換メカニズムは、Springで使用可能な古い永続性テンプレート(HibernateTemplate、JpaTemplateなど)でもデフォルトで有効になっています。

3. DataIntegrityViolationExceptionはどこにスローされますか

3.1. DataIntegrityViolationExceptionとHibernate

SpringがHibernateで構成されている場合、exceptionはSpringによって提供される例外変換レイヤー–SessionFactoryUtils – convertHibernateAccessExceptionでスローされます。

DataIntegrityViolationExceptionがスローされる原因となる可能性のあるHibernate例外は3つあります。

  • org.hibernate.exception.ConstraintViolationException

  • org.hibernate.PropertyValueException

  • org.hibernate.exception.DataException

3.2. DataIntegrityViolationException JPAあり

Springが永続プロバイダーとしてJPAを使用して構成されている場合、DataIntegrityViolationExceptionは、Hibernateと同様に、例外変換レイヤー、つまりEntityManagerFactoryUtils – convertJpaAccessExceptionIfPossibleでスローされます。

DataIntegrityViolationExceptionがスローされるトリガーとなる可能性のある単一のJPA例外(javax.persistence.EntityExistsException)があります。

4. 原因:org.hibernate.exception.ConstraintViolationException

これは、DataIntegrityViolationExceptionがスローされる最も一般的な原因です。休止状態のConstraintViolationExceptionは、操作がデータベースの整合性制約に違反していることを示します。

次の例を考えてみましょう–ParentエンティティとChildエンティティ間の明示的な外部キー列を介した1対1のマッピングの場合–次の操作は失敗するはずです。

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

ParentエンティティはChildエンティティへの外部キーを持っているため、子を削除すると親の外部キー制約が破られ、結果としてConstraintViolationExceptionが%でSpringによってラップされます。 (t3)s:

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

これを解決するには、最初にParentを削除する必要があります。

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

これは、DataIntegrityViolationExceptionの最も一般的な原因の1つです。Hibernateでは、これはエンティティが問題を抱えていることに起因します。 エンティティにnot-null constraintで定義されたnullプロパティがあるか、エンティティの関連付けがunsaved, transient instanceを参照している可能性があります。

たとえば、次のエンティティにはnull以外のnameプロパティがあります–

@Entity
public class Foo {
   ...

   @Column(nullable = false)
   private String name;

   ...
}

次のテストがnameのnull値でエンティティを永続化しようとした場合:

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

データベースの整合性制約に違反しているため、DataIntegrityViolationExceptionがスローされます。

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

HibernateDataExceptionは、無効なSQLステートメントを示します–その特定のコンテキストで、ステートメントまたはデータに問題がありました。 たとえば、以前のまたはFooエンティティを使用すると、次のようにこの例外がトリガーされます。

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

長いname値でオブジェクトを永続化するための実際の例外は、次のとおりです。

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)

この特定の例では、解決策は名前の最大長を指定することです:

@Column(nullable = false, length = 4096)

7. 原因:javax.persistence.EntityExistsException

Hibernateと同様に、EntityExistsException JPA例外もSpring Exception TranslationによってDataIntegrityViolationExceptionにラップされます。 唯一の違いは、JPA自体がすでに高レベルであるため、このJPA例外がデータ整合性違反の唯一の潜在的な原因になっていることです。

8. 潜在的にDataIntegrityViolationException

DataIntegrityViolationExceptionが予期される場合、別の例外がスローされることがあります。そのようなケースの1つは、hibernate-validator4または5などのJSR-303バリデーターがクラスパスに存在する場合です。

その場合、次のエンティティがnameのnull値で永続化されると、永続化レイヤーによってno longer fail with a data integrity violationがトリガーされます。

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

    ...
}

これは、実行が永続層に到達しないためです。その前に、javax.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. 結論

この記事の最後に、SpringでDataIntegrityViolationExceptionにつながる可能性のあるさまざまな原因と問題をナビゲートするための明確なマップと、これらすべての問題を修正する方法を十分に把握する必要があります。

すべての例外の例の実装はthe github projectにあります。これはEclipseベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。