21.2. Bidirectional one-to-many

Suppose we start with a simple <one-to-many> association from Parent to Child.

<set name="children">
    <key column="parent_id"/>
    <one-to-many class="Child"/>
</set>

If we were to execute the following code

Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();

Hibernate would issue two SQL statements:

This is not only inefficient, but also violates any NOT NULL constraint on the parent_id column. We can fix the nullability constraint violation by specifying not-null="true" in the collection mapping:

<set name="children">
    <key column="parent_id" not-null="true"/>
    <one-to-many class="Child"/>
</set>

However, this is not the recommended solution.

The underlying cause of this behaviour is that the link (the foreign key parent_id) from p to c is not considered part of the state of the Child object and is therefore not created in the INSERT. So the solution is to make the link part of the Child mapping.

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

(We also need to add the parent property to the Child class.)

Now that the Child entity is managing the state of the link, we tell the collection not to update the link. We use the inverse attribute.

<set name="children" inverse="true">
    <key column="parent_id"/>
    <one-to-many class="Child"/>
</set>

The following code would be used to add a new Child

Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c);
session.save(c);
session.flush();

And now, only one SQL INSERT would be issued!

To tighten things up a bit, we could create an addChild() method of Parent.

public void addChild(Child c) {
    c.setParent(this);
    children.add(c);
}

Now, the code to add a Child looks like

Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();