1. 概要

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

2. DataIntegrityViolationExceptionおよびSpring例外変換

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

<bean id="persistenceExceptionTranslationPostProcessor" 
   class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

またはJavaの場合:

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

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

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

3.1. DataIntegrityViolationException with Hibernate

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

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

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

3.2. DataIntegrityViolationExceptionJPAを使用

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

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

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

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

次の例を考えてみましょう– 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 –が発生します。 DataIntegrityViolationException でSpringによってラップされました:

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制約で定義されたnullプロパティを持っているか、エンティティの関連付けが未保存の一時インスタンスを参照している可能性があります。

たとえば、次のエンティティには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.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. 原因: org.hibernate.exception.DataException

Hibernate DataException は、無効な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例外も、SpringExceptionTranslationによってDataIntegrityViolationExceptionにラップされます。 唯一の違いは、JPA自体がすでに高レベルであるため、このJPA例外がデータ整合性違反の唯一の潜在的な原因となることです。

8. 潜在的にDataIntegrityViolationException

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

その場合、次のエンティティが name のnull値で永続化されると、永続化レイヤーによってトリガーされるデータ整合性違反失敗しなくなります。

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

    ...
}

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

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

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