Overview:
This post will look at how you map bi-directional one-to-many association. It's one of the most common association types that exists, and the semantics in mapping the association with cascade the parent/child relationship.
Hibernate Mapping:
This post will look at how you map bi-directional one-to-many association. It's one of the most common association types that exists, and the semantics in mapping the association with cascade the parent/child relationship.
Relationship:
1 0..m
| Parent | ----------------- | Child |
Database tables:
CREATE TABLE Parent(
parent_id INTEGER NOT NULL,
PRIMARY KEY (parent_id)
);
CREATE TABLE Child(
child_id INTEGER NOT NULL,
parent_id INTEGER NOT NULL,
PRIMARY KEY (child_id),
FOREIGN KEY (parent_id) REFERENCES Parent
);
POJOs:
package org.fazlan;
public class Parent {
private long id;
private Set children = new HashSet();
// getters and setters
}
package org.fazlan;
public class Child {
private long id;
private Parent parent;
// getters and setters
}
Hibernate Mapping:
Lets look at the basic mapping the parent and child entities,
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.fazlan">
<class name="Parent" table="parent">
. . .
<set name="children" inverse="true">
<key column="parent_id" />
<one-to-many class"Child" />
</set>
</class>
<class name="Child" table="child">
. . .
<many-to-one name="parent" column="parent_id" not-null="true"/>
</class>
</hibernate-mapping>
Now that the Child entity is managing the state of the link(parent_id), and we declare the Child entity is the one that manages the relationship. We use the 'inverse' attribute to do this.
The following code is used to add a Parent,
Parent p1 = new Parent(); session.save(p1); session.flush();
SQL generated by Hibernate,
Hibernate: select max(parent_id) from hibernatedb.Parent
Hibernate: insert into hibernatedb.Parent (parent_id) values (?)
The following code is used to add a 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();
Alternatively, you can add the following method to parent.
public void addChild(Child c) {
c.setParent(this);
getChildren().add(c);
}
Updated code will look like this,Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();
NOTE:
- When inverse="false" (default), Hibernate will generate TWO SQL statements: INSERT to create the child. UPDATE to create the link between parent and child.
<set name="children" inverse="false" >
<key column="parent_id" />
<one-to-many class"Child" />
</set>
SQL generated by Hibernate,
Hibernate: insert into hibernatedb.Child (parent_id, child_id) values (?, ?)
Hibernate: update hibernatedb.Child set parent_id=? where child_id=?
- When inverse="true", Hibernate generates ONLY ONE SQL statement: INSERT the child and the parent's id.
<set name="children" inverse="true" >
<key column="parent_id" />
<one-to-many class"Child" />
</set>
SQL generated by Hibernate,
Hibernate: insert into hibernatedb.Child (parent_id, child_id) values (?, ?)
Using Cascade associations:
You can get rid of the explicit call to the save() by using cascase attribute to the collection mapping.
<set name="children" inverse="true" cascase="all">
<key column="parent_id" />
<one-to-many class"Child" />
</set>
Simplified code,Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.flush();
This will insert the new child to the database. Also, the following code will now delete the parent and all it's children.
Parent p = (Parent) session.load(Parent.class, pid);
session.delete(p);
session.flush();
However, the following will not remove c from the database. In this case, it will only remove the link to p and cause a NOT NULL constraint violation.
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c); // remove link
c.setParent(null); // NOT NULL constraint violation
session.flush();
to achieve this, you need to explicitly call the delete() on the Child.
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();
But, a Child cannot exist without its parent. So if we remove a Child from the collection, we do want it to be deleted. To do this, we must use cascade="all-delete-orphan".
<set name="children" inverse="true" cascade="all-delete-orphan">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
Updated code, Now, removing the child from the collection would also will result in removing the child from the database
Parent p1 = (Parent) session.get(Parent.class,1l);
Child c1 = p1.getChildren().iterator().next();
p1.getChildren().remove(c1);
session.flush();
Summary:
This post looked at how you can map Parent/Child relationship efficiently in Hibernate, and also briefly looked at the 'inverse' and 'cascade' features.
Grate article, short and sweet....
ReplyDeleteKeep it up mate
Hi
ReplyDeleteCan you please help me in
http://stackoverflow.com/questions/18895585/hibernate-version-annotation-and-object-references-an-unsaved-transient-instanc
I want to update "chilrden" ?
ReplyDelete=>parent must update List
and children mus update parent
Im only thinking How to do it ?