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, вы также можете использовать отложенные ограничения, чтобы отложить проверку ограничений до тех пор, пока транзакция не будет выполнена. Не уверен, что/как это поддерживается другими базами данных.