Невозможно перейти в спящий режим, чтобы удалить дочерние экземпляры через каскад, которые были удалены из отношения один ко многим

Небольшая проблема здесь:

У меня есть два класса сущностей, скажем

class Parent {
    Set<Child> children;
}
class Child {
    SomethingElse reference;
}

Теперь отображение по существу:

<class name="Parent" lazy="false">
    <set name="children" lazy="false" cascade="all-delete-orphan">
        <key column="parent_id"/>
        <one-to-many class="Child"/>
    </set>
</class>

(здесь я опустил отображения идентификаторов и поля, я использую обычные идентификаторы)

По сути, мне нужно поддерживать чистую базу данных, поскольку, когда я удаляю элементы из списка родителя, а затем фиксирую родителя, соответствующие удаленные записи дочерней базы данных должны быть удалены. Дочерние экземпляры ссылаются на другие сущности, которые мне нужно будет удалить позже, поэтому, если дочерний экземпляр остается в базе данных, я не могу удалить эти ссылочные объекты.

То, что я обнаружил до сих пор: любая из вещей, которые я попробую ниже, должна работать, если я хочу сохранить упаковку PersistentCollection в hibernate. Проблема заключается в том, что мои объекты базы данных проходят через несколько уровней каркасов, которые включают в себя инфраструктуру пользовательского интерфейса, которая использует абстракции свойств бина для вызова сеттеров, и сетевой уровень связи, который клонирует и сериализует объекты назад и вперед. Оба этих слоя внутренне заменяют экземпляры коллекции и, таким образом, удаляют эти оболочки PersistentCollection. Переписать их, чтобы не делать это, не вариант.

Тем не менее, я попробовал 8 вещей, которые не сработали:

1) настроить отношение как cascade="all", использовать session.update (parent).

2) настроить отношение как cascade="all-delete-orphan", использовать session.update (parent).

3) настроить отношение как cascade = "all" и использовать session.merge(parent)

Все это приводит к тому, что hibernate выполняет "UPDATE CHILD SET parent.id = null WHERE parent.id = ...". Это успешно удаляет дочерние элементы из родительского списка при перезагрузке родительского экземпляра, но дочерний экземпляр остается в базе данных и не позволяет мне удалить другие ссылочные объекты.

4-6) использование конфигурации 1-3 с дополнительным определением столбца родительского ключа как ненулевого

Это приводит к тому, что hibernate ничего не делает. В другом посте я прочитал, что если сделать ключевой столбец ненулевым, это приведет к удалению. Звучит возможно, так как обновление до нуля больше не вариант, но не работает. Если я удаляю детей из коллекции, фиксирую изменения и перезагружаю экземпляр из базы данных, дети появляются снова.

7 + 8) родительский ключ, равный нулю или ненулевому, не имеет значения, но настройте отношение как cascade=all-delete-orphans и используйте session.merge(parent)

Это приводит к тому, что hibernate генерирует исключение "Коллекция с cascade =" all-delete-orphan "больше не ссылалась на экземпляр объекта-владельца" из-за удаленной оболочки PersistentCollection.

Чтобы решить мою проблему, единственное, что мне нужно, это Hibernate, чтобы выполнить запрос из опций 1-3 как УДАЛИТЬ вместо ОБНОВЛЕНИЯ. Я надеюсь, что просто не могу найти возможность настроить сопоставление таким образом, чтобы удалить их без упаковщиков PersistentCollection, но сейчас мне кажется, что такой опции нет. Кто-нибудь знает, есть ли способ настроить это?

/ edit: Чтобы уточнить, пример того, что я хочу, чтобы произошло:

Parent parent = new Parent();
parent.setChildren(new HashSet<Child>(Arrays.asList(new Child()))));
session.insert(parent)
// this correctly results in (approximately):
// SQL> INSERT INTO PARENT ...
// SQL> INSERT INTO CHILD ...

parent.setChildren(new HashSet<Child>()); // using .clear() is not an option.
session.update(parent);
// this results in:
// SQL> UPDATE CHILD set parent_id = null WHERE parent_id = ${id.of.parent}
// but i need this to result in:
// SQL> DELETE FROM CHILD WHERE parent_id = ${id.of.parent}

2 ответа

Хорошо, я, видимо, исправил это сейчас. Проблема заключалась в том, что я назначал не пустой набор, а ноль. По-видимому, в случае session.merge (обновлено) hibernate внезапно различает пустые коллекции и нулевые коллекции. Использование cascade="all-delete-orphan" и.merge() с пустыми экземплярами коллекции, назначенными свойствам, работает, назначая пустое значение вместо пустого экземпляра коллекции, чтобы вызвать упомянутое исключение. Это то же самое независимо от ограничений обнуляемости для ключевого столбца.

Я не знаю, считается ли это преднамеренным поведением, поскольку обычно нулевые значения действуют так же, как пустые коллекции. Я посмотрю, смогу ли я узнать об этом больше, а затем, возможно, выложу отчет об ошибке.

обновление: проблема на https://hibernate.atlassian.net/browse/HHH-7726

Это не полностью отвечает на ваш вопрос, но я надеюсь, что это может немного помочь.
Во-первых, я бы порекомендовал вам взглянуть на это объяснение, а также на это.

Теперь вы сами сказали, что дочерние объекты не ссылаются на родителя, и что это односторонние отношения. Я не знаю, какой вид картирования вы придумали, но это:

Parent parent = new Parent();
parent.setChildren(Collections.singleton(new Child())));
session.save(parent);
// this correctly results in:
// SQL> INSERT INTO PARENT ...
// SQL> INSERT INTO CHILD ...

Работать можно, только если:

  • cascade в наборе включено отображение (например cascade="all"иначе Hibernate будет жаловаться на несохраненный временный экземпляр нового Child объект)
  • столбец parent_id имеет значение NULL (иначе Hibernate будет ожидать, что вы зададите это поле, что возможно только в том случае, если у вас есть другая сторона взаимосвязи)

Кроме того, Hibernate сгенерирует еще один SQL UPDATE в дополнение к этим двум вставкам, которые вы упомянули в комментариях (то, что вы можете увидеть, на самом деле объясняется в ссылках, которые я вам дал).

Надеюсь, ты что-то из этого получишь.

Другие вопросы по тегам