清除关联子项列表后的会话更新不会删除它们

时间:2013-06-14 19:13:29

标签: java hibernate java-ee one-to-many

我正在使用Hibernate 2.1.8以及以下父子关系:

public class Parent     {
    private Set<Child> children = new HashSet<Child>();
}

public class Child{
    private Parent parent;
}

Hibernate映射看起来像:

<hibernate-mapping>
    <class name="Parent" table="parent">
        <id name="id" column="parent_id" unsaved-value="0" type="int"> 
            <generator
                class="com.mx.releasemgr.db.hibernate.HibernateIdentityGenerator" />
        </id>
...
        <set name="children" table="child" inverse="false" cascade="all-delete-orphan">
            <key column="parent_id" />
            <one-to-many class="child"/>
        </set>   
    </class>
</hibernate-mapping>

<hibernate-mapping>
    <class name="com.mx.releasemgr.domain.ImplementationProjectChange" table="implementation_project_change" >
        <id name="id" column="implementation_project_change_id" unsaved-value="0" type="int">
            <generator class="com.mx.releasemgr.db.hibernate.HibernateIdentityGenerator"/>
        </id>

        <many-to-one name="parent" column="parent_id" class="Parent" not-null="true"/>
    </class>
</hibernate-mapping>

如果执行以下代码,则子(ren)的parent_id列仅设置为NULL,但不删除记录。我希望儿童记录也被删除,但无法弄明白。

parent.getChildren().clear();
session.update(parent);

如果我使用空集合更新父级,如何删除子项?

3 个答案:

答案 0 :(得分:2)

在Hibernate中,如果你有一个ManyToOne,那么它总是一个关系的一流拥有方(除非你的ManyToOne由一个连接表而不是多边的列表示)。换句话说,没有办法在关系的多边设置inverse=true

当您创建OneToMany时,如果您指定inverse=false,那么您将创建该关系的另一个一流拥有方。

你创建的是一个无效的场景,其中Hibernate认为有两个完全独立的关系并且没有将它们链接在一起。

以下方案有效:

  • 只允许OneToMany使用inverse=false
  • 只有ManyToOne
  • 在OneToMany上同时拥有OneToMany和ManyToOne inverse=true
  • 使用联接表

我不知道双方是否拥有双向关系的方式。如果您想要双向关系,可能最简单的解决方法是在inverse=true类上设置Parent,然后将您的代码调整为:

for(ImplementationProjectChange child : parent.getChildren()) { 
  child.setParent(null);
}
parent.getChildren().clear();
session.update(parent); 

答案 1 :(得分:2)

  1. 双向关系&amp;反向财产

    当单个外键(FK)被映射为双向关系时,一方的关系应该管理FK(inverse=false),另一方的关系不应该对FK做任何事情({{1 }})。

    在您的情况下,双方都是inverse=true,因为这是子inverse=false关系的默认值。父母&amp;孩子互相竞争以修改同一列的数据。他们认为他们每个人都在独立的FK上运作;他们不知道他们写的是同一个FK专栏。如果您没有以编程方式保持关系两侧的数据100%一致(父对象的关系属性v子对象的关系属性),那么您将在数据库中获得数据错误。

  2. 谁应该管理FK:父母或子女?

    孩子应该。

    子管理关系时,一切顺利,SQL最小,并且始终满足参照完整性。如果创建了子实例并将其链接到父实例,则插入新行,并将FK作为单个操作包含在内。同样的更新&amp;删除。

    父管理关系时,需要额外的SQL语句。如果创建子实例并将其链接到父实例,则首先执行子操作 - 插入子行。接下来,父操作发生 - 一个单独的更新,为它的“远程”FK列设置FK值,该列实际上存在于子表中。同样的更新&amp;删除。 在某些情况下,可能会发生数据库约束违规,因此操作失败。子插入必须始终包含空FK列。如果DB列是NOT NULL或者FK列也是PK的一部分(不是在你的情况下),它会因为违反DB参照完整性而导致DB错误 - insert语句将失败并且不会发生更新。

    < / LI>
  3. 最终答案

    在父实体many-to-one映射中:set
    (您已经正确拥有inverse="true")。

    您已经拥有正确的子实体cascade="all-delete-orphan"映射(保留many-to-one,默认值,并离开inverse=false,这意味着孩子必须始终与其父级有关系,并且不能存在分开)。

答案 2 :(得分:1)

你应该采取另一种提取策略 在您的示例中,您使用的是默认提取策略(select) 您无法使用此策略删除对象。使用joinsubselect策略:

    <set name="children" table="child" inverse="false" 
       fetch="join" cascade="all-delete-orphan">

    <set name="children" table="child" inverse="false" 
        fetch="subselect" cascade="all-delete-orphan">