Spring DataIntegrityViolationException

Spring DataIntegrityViolationException

1. обзор

В этой статье мы обсудим Springorg.springframework.dao.DataIntegrityViolationException - это общее исключение данных, обычно вызываемое механизмом преобразования исключений Spring при работе с исключениями сохраняемости более низкого уровня. В статье будут обсуждаться наиболее распространенные причины этого исключения, а также решение для каждого из них.

Дальнейшее чтение:

Поддержка Spring Data Java 8

Краткое и практическое руководство по поддержке Java 8 в Spring Data.

Read more

Spring Data Annotations

Узнайте о наиболее важных аннотациях, которые нам нужны для обеспечения сохранности с помощью проекта Spring Data.

Read more

Работа с отношениями в Spring Data REST

Практическое руководство по работе со связями сущностей в Spring Data REST.

Read more

2. DataIntegrityViolationException и перевод исключений Spring

Механизм трансляции исключений Spring может быть прозрачно применен ко всем bean-компонентам, аннотированным с помощью@Repository - путем определения bean-компонента постпроцессора bean-компонента преобразования исключений в контексте:

Или на Java:

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

Механизм преобразования исключений также включен по умолчанию в старом шаблоне сохраняемости, доступном в Spring - HibernateTemplate, JpaTemplate и т. Д.

3. Где выброшенDataIntegrityViolationException

3.1. DataIntegrityViolationException с гибернацией

Когда Spring настроен с Hibernate,exception выбрасывается на уровне преобразования исключений, предоставляемом Spring -SessionFactoryUtils – convertHibernateAccessException.

Есть три возможных исключения Hibernate, которые могут вызвать выбросDataIntegrityViolationException:

  • org.hibernate.exception.ConstraintViolationException

  • org.hibernate.PropertyValueException

  • org.hibernate.exception.DataException

3.2. DataIntegrityViolationException с JPA

Когда Spring настроен с использованием JPA в качестве поставщика сохраняемости,DataIntegrityViolationException выбрасывается, как и Hibernate, на уровне преобразования исключений, а именно вEntityManagerFactoryUtils – convertJpaAccessExceptionIfPossible.

Существует единственное исключение JPA, которое может вызвать выбросDataIntegrityViolationException -javax.persistence.EntityExistsException.

4. Причина:org.hibernate.exception.ConstraintViolationException

Это, безусловно, наиболее частая причина выбросаDataIntegrityViolationException - HibernateConstraintViolationException указывает, что операция нарушила ограничение целостности базы данных.

Рассмотрим следующий пример - для сопоставления «один-к-одному» через явный столбец внешнего ключа между сущностямиParent иChild - следующие операции должны завершиться ошибкой:

@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 в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

Чтобы решить эту проблему, сначала следует удалить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 - в Hibernate это сводится к тому, что сущность сохраняется с проблемой. Либо объект имеет свойство null, которое определяется с помощьюnot-null constraint, либо ассоциация объекта может ссылаться наunsaved, transient instance.

Например, следующая сущность имеет ненулевое свойствоname -

@Entity
public class Foo {
   ...

   @Column(nullable = false)
   private String name;

   ...
}

Если следующий тест пытается сохранить сущность с нулевым значением дляname:

@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 - что-то не так с оператором или данными в этом конкретном контексте. Например, использование объекта orFoo из предыдущего примера вызовет это исключение:

@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, может возникнуть другое исключение - например, если в пути к классам существует валидатор JSR-303, такой какhibernate-validator 4 или 5.

В этом случае, если следующая сущность сохраняется с нулевым значением дляname, она будет запускаться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. Выводы

В конце этой статьи у нас должна быть четкая карта для навигации по разнообразию причин и проблем, которые могут привести кDataIntegrityViolationException в Spring, а также хорошее представление о том, как исправить все эти проблемы.

Реализацию всех примеров исключений можно найти вthe github project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.