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 for c
an UPDATE
to create the link from p
to
c
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();