1

I am using Hibernate 2.1.8 with the following parent child relationship:

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

public class Child{
    private Parent parent;
}

The Hibernate mappings look like:

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

If the following code is executed, the parent_id column(s) of the child(ren) is only set to NULL, but the record is not deleted. I would like the child records to also be deleted, but can't figure it out.

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

How can I make the children be deleted if I update the parent with an empty collection?

Bizmarck
  • 2,663
  • 2
  • 33
  • 48

3 Answers3

2

In Hibernate, if you have a ManyToOne then it is always a first-class owning side of a relationship (unless possibly if your ManyToOne is represented by a join table and not a column on the many side). In other words, there is no way to set inverse=true on the Many side of the relationship.

When you create a OneToMany, if you specify inverse=false then you are creating another first-class owning side of the relationship.

What you have created is an invalid scenario where Hibernate thinks there are two completely separate relationships and hasn't linked them together.

The following scenarios are valid:

  • Only have the OneToMany with inverse=false
  • Only have the ManyToOne
  • Have both the OneToMany and the ManyToOne with inverse=true on the OneToMany
  • Use a join table

I don't know of any way to have a bidirectional relationship where the one side is the owning side. Probably the easiest fix, if you want the bidirectional relationship, is to set inverse=true on the Parent class and then adjust your code to be:

for(ImplementationProjectChange child : parent.getChildren()) { 
  child.setParent(null);
}
parent.getChildren().clear();
session.update(parent); 
Pace
  • 41,875
  • 13
  • 113
  • 156
2
  1. Bidirectional Relationships & the Inverse Property

    When a single foreign key (FK) is mapped as a bidirectional relationship, the relationship on one side should manage the FK (inverse=false) and the relationship on other side should do nothing to the FK (inverse=true).

    In your case, inverse=false on both sides, because this is the default for a child many-to-one relationship. The parent & child are competing against each other to modify data for the same column. They think they are each operating on independent FKs; they don't know they are writing to the same FK column. If you don't programmatically keep the data on both sides of the relationship 100% consistent (parent object's relationship property v child object's relationship property), then you will get data errors in the DB.

  2. Who Should Manage the FK: Parent or Child?

    The child should.

    When the child manages the relationship, all goes smooth with minimal SQL and with referential integrity always satisfied. If a child instance is created and linked to the parent, the new row is inserted with the FK included as a single operation. Similarly for updates & deletes.

    When the parent manages the relationship, extra SQL statements are required. If a child instance is created and linked to the parent, the child's operation occurs first - a child row is inserted. Next, the parent's operation occurs - a separate update to set the FK value for it's "remote" FK column, which actually lives in the child table. Similarly for updates & deletes. Under some conditions, DB constraint violation can occur and hence operations fail. The child insert must always include null FK column. If the DB column is NOT NULL or the FK column is also part of the PK (not in your case), it would give DB error for violating DB referential integrity - insert statement would fail and update would not occur.

  3. Final Answer:

    In the parent entity set mapping: inverse="true".
    (You already correctly have cascade="all-delete-orphan").

    You already have the correct child entity many-to-one mapping (leave inverse=false, the default, and leave not-null=true, meaning the child must always have a relationship to it's parent, and cannot exist separately).

Glen Best
  • 22,769
  • 3
  • 58
  • 74
1

You should another fetching strategy.
In you example, you are using default fetch strategy (select).
You cann't delete objects with this strategy. Use join or subselect strategy:

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

or

    <set name="children" table="child" inverse="false" 
        fetch="subselect" cascade="all-delete-orphan">  
Ilya
  • 29,135
  • 19
  • 110
  • 158
  • I don't see a `fetch="subselect"` or `fetch="join"` in the DTD for hibernate. I tried `outer-join="true"` but this had no effect. – Bizmarck Jun 17 '13 at 15:13