Как справиться с нарушениями уникальных ограничений JPA?

Когда уникальное ограничение нарушается, javax.persistence.RollbackException брошен Но может быть несколько причин, чтобы бросить RollbackException, Как я могу узнать, что уникальное ограничение было нарушено?

try {
    repository.save(article);
}
catch(javax.persistence.RollbackException e) {
    // how to find out the reason for the rollback exception?
}

9 ответов

Решение

Как я могу узнать, что уникальное ограничение было нарушено?

Исключение приковано, вы должны позвонить getCause() рекурсивно, чтобы получить конкретное исключение поставщика (и, возможно, перейти к SQLException) чтобы перевести его в то, что ваше приложение может обработать для вашего пользователя. Следующее напечатает цепочку исключений:

for (t = e.getCause(); t != null; t = t.getCause()) {
    logger.debug("Exception:" + t);
}

Для обработки исключений и "перевода" вы можете сделать что-то вроде того, что делает Spring (см. Различные JpaDialect классы, например HibernateJpaDialectчтобы получить представление).

Все это нехорошо, этот код не будет переносимым, и найти атрибуты, вызвавшие нарушение, будет непросто. Это как-то подтверждает, что в JPA нет элегантного и портативного способа обработки нарушений ограничений.

Вы можете использовать следующее:

Если вы используете Spring Framework, то вы можете использовать:

org.springframework.dao.DataIntegrityViolationException

Если можно использовать стандарт JPA

org.hibernate.exception.ConstraintViolationException

На уровне sql можно использовать следующее:

java.sql.SQLIntegrityConstraintViolationException

Использование e.getCause() изучить, что послужило причиной отката.

Это может быть очень поздно для вас, но вот как я решил это для PostGres.

catch (DataIntegrityViolationException e) {

            for (Throwable t = e.getCause(); t != null; t = t.getCause()) {

                if (PSQLException.class.equals(t.getClass())) {
                    PSQLException postgresException = (PSQLException) t;

                    // In Postgres SQLState 23505=unique_violation
                    if ("23505".equals(postgresException.getSQLState())) {
                        throw new CustomDataAlreadyExistsException("YourErrorCode", e);
                    }
                }
            }
            throw new SomeOtherException("YourErrorCode2", e);

        }

Компилятор возвращает исключение SQLIntegrityConstraintViolationException при попытке нарушить уникальное ограничение.

Используйте следующую концепцию блока catch для обработки правильных исключений.

catch(SQLIntegrityConstraintViolationException e) 
{
  // Error message for integrity constraint violation
}
catch(Exception e)
{
 // Other error messages
}

Вы можете сделать так:

StringWriter writer=new StringWriter(); //remains the message from stack trace.
e.printStackTrace(new PrintWriter(writer));
String message=writer.toString(); // gets the message of full stack trace.

А затем просмотрите информацию об исключении.

Я думаю, печать трассировки стека поможет вам узнать это. e.printStackTrace();

Вам нужно поймать javax.persistence.PersistenceException. Выброшенное исключение - org.hibernate.exception.ConstraintViolationException.

Сообщение об исключении: org.hibernate.exception.ConstraintViolationException: повторяющаяся запись 893073116V-1234567 для ключа nic_account_no

Самый простой способ определить причину нарушения ограничения — это, во-первых, правильно определить имя ограничения :

Определение уникального ограничения:

С использованием@UniqueConstraintвнутри@Table, мы можем определить ограничения для одного столбца или нескольких столбцов вместе с пользовательским именем ограничения:

      @Entity
@Table(uniqueConstraints = {
        @UniqueConstraint(columnNames = { "name" }, name = "name_constraint"),
        @UniqueConstraint(columnNames = { "teamId", "orderIndex" }, name = "teamId_orderIndex_constraint")
})
public class Space {
    // .. other field omitted

    private String name;

    private Integer orderIndex;

    private Integer teamId;
}

JPA изменяет ограничение с нашим пользовательским именем:

      Hibernate: 
    alter table space
       add constraint name_constraint unique (name)

Hibernate: 
    alter table space
       add constraint teamId_orderIndex_constraint unique (team_id, order_index)

Правильно обращатьсяDataIntegrityViolationException:

С использованиемe.getMessage(), мы получаем результат, который содержит имя ограничения, которое мы определили выше:

      could not execute statement; SQL [n/a]; constraint [space.name_constraint]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement

Затем просто проверьте, содержит ли результат имя, которое мы определили:

      import org.springframework.dao.DataIntegrityViolationException;

try {
    repository.save(space);
} catch (DataIntegrityViolationException e) {

    if(e.getMessage().contains("name_constraint")) {
        System.out.println("Unique name constraint violated");
    }

    if(e.getMessage().contains("teamId_orderIndex_constraint")) {
        System.out.println("Unique teamId_orderIndex constraints violated");
    }
}

Ссылка: Определение уникальных ограничений в JPA

Другие вопросы по тегам