0

这里有一个小问题:

我有两个实体类,比方说

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>

(我在这里省略了 id 映射和字段,我使用常规生成的 id)

我基本上需要保持一个干净的数据库,就像当我从父列表中删除元素然后提交父时一样,需要删除相应删除的子数据库条目。Child 实例引用了我以后需要删除的其他实体,因此如果子实例保留在数据库中,我将无法删除那些引用的对象。

到目前为止我发现了什么:如果我要保持 hibernate 的 PersistentCollection 包装器就位,我在下面尝试的任何事情都应该有效。问题是,我的数据库对象来自几层框架,其中包括一个 UI 框架,它使用 bean 属性抽象来调用 setter,以及一个来回克隆和序列化对象的网络通信层。这两个层都在内部替换了集合实例,因此删除了这些 PersistentCollection 包装器。重写这些以不这样做不是一种选择。

也就是说,我尝试过的 8 件事没有奏效:

1) 将关系配置为 cascade="all",使用 session.update(parent)。

2) 将关系配置为 cascade="all-delete-orphan",使用 session.update(parent)。

3) 将关系配置为 cascade="all" 并使用 session.merge(parent)

所有这些都会导致休眠执行“UPDATE CHILD SET parent.id = null WHERE parent.id = ...”。这在重新加载父实例时成功地从父列表中删除了子实例,但子实例保留在数据库中并阻止我删除其他引用的实体。

4-6) 使用配置 1-3,同时将父键列定义为非空

这导致休眠不做任何事情。我在另一篇文章中读到,使键列非空会导致删除。听起来可能,因为更新为 null 不再是一种选择,但不起作用。如果我从集合中删除孩子,提交更改并从数据库重新加载实例,孩子会重新出现。

7+8) 父键可空或非空无关紧要,但将关系配置为 cascade=all-delete-orphans 并使用 session.merge(parent)

由于删除了 PersistentCollection 包装器,这会导致 hibernate 抛出异常“具有 cascade="all-delete-orphan" 的集合不再被拥有的实体实例引用”。

为了解决我的问题,我唯一需要的是休眠来执行选项 1-3 中的查询作为删除而不是更新。我希望我只是无法找到以在没有 PersistentCollection 包装器的情况下删除这些映射的方式来配置映射的选项,但在我看来,目前似乎没有这样的选项。有谁知道是否有办法配置这个?

/编辑:为了澄清,我想要发生的事情的例子:

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}
4

2 回答 2

1

好吧,我现在显然修好了。问题是我没有分配空集,而是空集。显然,在 session.merge(updated) 的情况下,hibernate 突然区分了空集合和空集合。将 cascade="all-delete-orphan" 和 .merge() 与分配给属性的空集合实例一起使用,分配 null 而不是空集合实例会引发上述异常。无论键列上的可空性约束如何,这都是相同的。

我不知道这是否被认为是故意行为,因为通常空值与空集合的行为方式相同。我会看看我是否能找到更多关于这个的信息,然后可能会提出一个错误报告。

更新:问题在https://hibernate.atlassian.net/browse/HHH-7726

于 2012-10-26T21:47:31.617 回答
0

这并不能完全回答您的问题,但我希望它可以提供一点帮助。
首先,我建议你看看这个解释,以及这个

现在,您自己说过子对象不引用父对象,这是一种单向关系。我不知道你想出了什么样的映射,但是这个:

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 列可以为空(否则 Hibernate 会期望您预设此字段,这只有在您映射了关系的另一端时才有可能)

另外,除了您在评论中提到的这两个插入之外,Hibernate 还会生成一个SQL UPDATE(您所看到的实际上在我给您的链接中进行了解释)。

希望你能从中有所收获。

于 2012-10-26T10:44:23.150 回答