Я пытаюсь написать метод, который вернет объект Hibernate на основе уникального, но не первичного ключа. Если объект уже существует в базе данных, я хочу его вернуть, но если это не так, я хочу создать новый экземпляр и сохранить его перед возвратом.
ОБНОВЛЕНИЕ: Позвольте мне пояснить, что приложение, для которого я пишу это, - это в основном пакетный процессор входных файлов. Системе необходимо прочитать файл по строкам и вставить записи в db. Формат файла в основном является денормализованным представлением нескольких таблиц в нашей схеме, поэтому мне нужно проанализировать родительскую запись или вставить ее в db, чтобы я мог получить новый синтетический ключ, или если он уже существует, выберите его. Затем я могу добавить дополнительные связанные записи в другие таблицы, которые имеют внешние ключи обратно к этой записи.
Причина, по которой это сложно, заключается в том, что каждый файл должен быть полностью импортирован или вообще не импортирован, т.е. все вставки и обновления, сделанные для данного файла, должны быть частью одной транзакции. Это достаточно легко, если есть только один процесс, который выполняет все импортные операции, но я хотел бы разбить его на нескольких серверах, если это возможно. Из-за этих ограничений мне нужно иметь возможность оставаться внутри одной транзакции, но обрабатывать исключения, в которых запись уже существует.
Отображаемый класс для родительских записей выглядит следующим образом:
@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 лучший способ выполнить это?
Позвольте мне также объяснить, почему я сначала вставляю, а затем выбираю, когда и когда это произойдет. Это должно работать в распределенной среде, поэтому я не могу выполнить синхронизацию по проверке, чтобы убедиться, что запись уже существует и вставка. Самый простой способ сделать это - позволить базе данных обрабатывать эту синхронизацию, проверяя нарушение ограничений на каждую вставку.