Я пытаюсь написать метод, который будет возвращать объект Hibernate на основе уникального, но не первичного ключа. Если сущность уже существует в базе данных, я хочу вернуть ее, но если ее нет, я хочу создать новый экземпляр и сохранить его перед возвратом.
ОБНОВЛЕНИЕ: Позвольте мне уточнить, что приложение, для которого я пишу это, является в основном пакетным процессором входных файлов. Система должна построчно читать файл и вставлять записи в БД. Формат файла в основном представляет собой денормализованное представление нескольких таблиц в нашей схеме, поэтому мне нужно разобрать родительскую запись, либо вставить ее в базу данных, чтобы я мог получить новый синтетический ключ, либо, если он уже существует, выбрать его. Затем я могу добавить дополнительные связанные записи в другие таблицы с внешними ключами обратно в эту запись.
Причина, по которой это становится сложным, заключается в том, что каждый файл должен быть либо полностью импортирован, либо не импортирован вообще, то есть все вставки и обновления, выполненные для данного файла, должны быть частью одной транзакции. Это достаточно просто, если есть только один процесс, который выполняет весь импорт, но я бы хотел разбить его на несколько серверов, если это возможно. Из-за этих ограничений мне нужно иметь возможность оставаться внутри одной транзакции, но обрабатывать исключения, где запись уже существует.
Сопоставленный класс для родительских записей выглядит следующим образом:
@Entity
public class Foo {
@Id
@GeneratedValue(strategy = IDENTITY)
private int id;
@Column(unique = true)
private String name;
...
}
Моя первоначальная попытка написания этого метода следующая:
public Foo findOrCreate(String name) {
Foo foo = new Foo();
foo.setName(name);
try {
session.save(foo)
} catch(ConstraintViolationException e) {
foo = session.createCriteria(Foo.class).add(eq("name", name)).uniqueResult();
}
return foo;
}
Проблема в том, что когда искомое имя существует, возникает исключение org.hibernate.AssertionFailure при вызове uniqueResult (). Полная трассировка стека ниже:
org.hibernate.AssertionFailure: null id in com.searchdex.linktracer.domain.LinkingPage entry (don't flush the Session after an exception occurs)
at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:82) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:190) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:147) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1185) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1709) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.CriteriaImpl.uniqueResult(CriteriaImpl.java:369) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
Кто-нибудь знает, что вызывает выбрасывание этого исключения? Hibernate поддерживает лучший способ сделать это?
Позвольте мне также предупреждающе объяснить, почему я сначала вставляю, а затем выбираю, если и когда это не удастся. Это должно работать в распределенной среде, поэтому я не могу синхронизировать проверку, чтобы увидеть, существует ли уже запись и вставка. Самый простой способ сделать это - позволить базе данных обрабатывать эту синхронизацию, проверяя нарушение ограничений при каждой вставке.