Hibernate - очистка коллекции с помощью all-delete-orphan и добавление к ней вызывает ConstraintViolationException
У меня есть эти сущности
class Foo{
Set<Bar> bars;
}
class Bar{
Foo parent;
String localIdentifier;
}
С этим сопоставлением (извините, никаких аннотаций, я старомодный):
<class name="Foo">
...
<set name="bars" cascade="all-delete-orphan" lazy="false" inverse="true">
<key>...</key>
<one-to-many class="Bar"/>
</set>
</class>
<class name="Bar">
...
<property name="localIdentifier" column="local_identifier"/>
<many-to-one name="parent" column="parent_id" />
</class>
У меня также есть уникальное ограничение на 2 столбца: local_identifier
и parent_id
(а не единственное ограничение на каждом из них, но допускается единственный уникальный ограничитель, содержащий оба, например, не 2 строки с одним и тем же родителем и одним и тем же локальным идентификатором)
alter table bar add constraint unique_bar unique (parent_id, local_identifier)
И этот код, который их использует:
//foo is persistent, foo id = 1
Bars bars = foo.getBars();
bars.clear(); // bars contained 1 item [parent_id = 1, local_identifier = "a"]
Bar newBar = new Bar();
newBar.setParent(foo);
newBar.setLocalIdentifier("a");
bars.add(newBar);
Теперь по какой-то причине Hibernate не выполняет вещи в том порядке, в котором они были вызваны. Он не выполняет clear()
(удаление) перед add()
(insert), но наоборот, он сначала пытается вставить, получив ConstraintViolationException
Я знаю, что добавить немного session.flush()
после bars.clear();
, смог бы исправить это, но в этом случае у меня нет доступа к сеансу не уродливым способом.
Итак, единственное решение - это флеш? или существует версия Hibernate, которая учитывает порядок действий?
Update:
Кстати, разыменование коллекции приведет к исключению HibernateException из https://www.hibernate.org/117.html#A3:
Я получаю исключение HibernateException: не разыгрывать коллекцию с cascade = "all-delete-orphan" Это произойдет, если вы загрузите объект с помощью cascade = "all-delete-orphan" коллекции, а затем удалить ссылка на сборник. не замените эту коллекцию, используйте clear() поэтому алгоритм удаления сирот может обнаружите изменения.
Ответы
Ответ 1
Я думаю, что нет никакой альтернативы промывке
Из здесь:
Hibernate нарушает уникальное ограничение!
Спящий режим не совсем такой умный с уникальными ограничениями, поскольку он с иностранными ключами. Иногда вы может потребоваться дать небольшой намек.
Единственное нарушение ограничений может возникают, если два объекта являются одновременно обновлено, одно - "освобождение" значения а другой - "получение" того же стоимость. Обходным путем является flush()сеанса вручную после обновления первого объекта и до обновления второй.
(Эта проблема возникает редко в практика).
Ответ 2
Если вы хотите избежать промывки сеанса здесь, попробуйте заменить весь список (new List<Bar>()
вместо Clear()
). Hibernate должен фактически удалить все элементы за один кадр, прежде чем добавлять новые. Просто попробуйте, не уверен, что он работает.
Ответ 3
Если вы используете oracle, вы также можете использовать отложенные ограничения, чтобы отложить проверку ограничений до тех пор, пока транзакция не будет выполнена. Не уверен, что/как это поддерживается другими базами данных.