Spring DataIntegrityViolationException

1. Общ преглед

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

2. Превод на DataIntegrityViolationException и Spring Exception

Механизмът за превод на Spring Spring може да се приложи прозрачно към всички зърна, коментирани с @Repository - чрез дефиниране на зърно за процесор на преобразуване на изключение в контекста:

Или в Java:

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

Механизмът за превод на изключения също е активиран по подразбиране за по-стария шаблон за постоянство, наличен през пролетта - HibernateTemplate, JpaTemplate и др

3. Къде е хвърлен DataIntegrityViolationException

3.1. DataIntegrityViolationException с хибернация

Когато Spring е конфигуриран с Hibernate, изключението се хвърля в слоя за превод на изключения, предоставен от Spring - SessionFactoryUtils - convertHibernateAccessException .

Има три възможни изключения за хибернация, които могат да доведат до изхвърляне на DataIntegrityViolationException :

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

3.2. DataIntegrityViolationException с JPA

Когато Spring е конфигуриран с JPA като доставчик на постоянство, DataIntegrityViolationException се хвърля, подобно на Hibernate, в слоя за превод на изключения - а именно в EntityManagerFactoryUtils - convertJpaAccessExceptionIfPossible .

Има един-единствен на СПА изключение, което може да предизвика DataIntegrityViolationException да бъдат изхвърлени - на javax.persistence.EntityExistsException .

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

Това е най-честата причина за изхвърляне на DataIntegrityViolationException - Hibernate ConstraintViolationException показва, че операцията е нарушила ограничението за целостта на базата данни.

Помислете за следния пример - за съпоставяне Едно към едно чрез изрична колона с външен ключ между обекти на родител и дете - следните операции трябва да не успеят:

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

Обектът родител има външен ключ към обекта 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

За да се реши това, първо трябва да бъде изтрит родителят :

@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 - в режим на хибернация това ще стигне до обект, който продължава да съществува с проблем. Или обектът има нулево свойство, което е дефинирано с ненулево ограничение , или асоциация на обекта може да препраща към незаписан, преходен екземпляр .

Например, следният обект има свойство не-null име -

@Entity public class Foo { ... @Column(nullable = false) private String 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.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

Хибернация на DataException показва невалидно SQL изявление - нещо не е наред с израза или данните в този конкретен контекст. Например, използвайки или Foo обект от преди, следното ще задейства това изключение:

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

Действителното изключение за запазване на обекта със стойност на дълго име е:

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

Подобно на хибернация, изключението JPA на EntityExistsException също ще бъде обгърнато от Spring Exception Translation в DataIntegrityViolationException . Единствената разлика е, че самата JPA вече е на високо ниво, което прави това JPA изключение единствената потенциална причина за нарушения на целостта на данните.

8. Потенциално DataIntegrityViolationException

В някои случаи, когато може да се очаква DataIntegrityViolationException, може да се изведе друго изключение - един такъв случай е, ако валидатор JSR-303, като hibernate-validator 4 или 5 съществува на пътя на класа.

В този случай, ако следният обект се запази с нулева стойност за име , той вече няма да се провали с нарушение на целостта на данните, задействано от слоя за постоянство:

@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. Заключения

В края на тази статия трябва да имаме ясна карта, за да се ориентираме в разнообразието от причини и проблеми, които могат да доведат до DataIntegrityViolationException през пролетта, както и добро разбиране за това как да отстраним всички тези проблеми.

Прилагането на всички примери за изключения може да бъде намерено в проекта github - това е проект, базиран на Eclipse, така че трябва да е лесно да се импортира и да се изпълнява както е.