Как справиться с нарушениями уникальных ограничений 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");
}
}