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:
- an
INSERT
to create the record forc
- an
UPDATE
to create the link fromp
toc
This is not only inefficient, but also violates any
NOT NULL
constraint on the parent_id
column. You 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 behavior 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
. The solution is to make the link part of the Child
mapping.
<many-to-one name="parent" column="parent_id" not-null="true"/>
You 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 to do this:
<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();
Only one SQL
INSERT
would now be issued.
You could also create an
addChild()
method of Parent
.
public void addChild(Child c) { c.setParent(this); children.add(c); }
The code to add a
Child
looks like this:
Parent p = (Parent) session.load(Parent.class, pid); Child c = new Child(); p.addChild(c); session.save(c); session.flush();