Recursive relations are possible. Typical example are Trees where each node can have a subtree. We will create a Tree where each TreeItem knows its parent and its children. Full source code is provided in the package: de.laliluna.relation.recursive
Classes | Tables |
Annotation mapping.
import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; ....... snip ...... @Entity public class TreeItem implements Serializable { @ManyToOne @JoinColumn(name="parent_id") private TreeItem parent; @OneToMany(mappedBy="parent", cascade = CascadeType.ALL) private Set<TreeItem> children = new HashSet<TreeItem>();
The relation is in fact a simple one-to-many relation. @OneToMany(mappedBy="parent", cascade = CascadeType.ALL) specifies that the relation is managed by the parent property. @ManyToOne specifies the relation @JoinColumn(name="parent_id") specifies which column is the foreign key column to the TreeItem primary key.
XML mapping.
<hibernate-mapping package="de.laliluna.example6"> <class name="TreeItem" table="ttreeitem" > ........ snip ...... <many-to-one name="parent" class="TreeItem" unique="true" not-null="false" > <column name="parent_id" ></column> </many-to-one> <set name="children" inverse="true" cascade="all,delete-orphan"> <key column="parent_id"></key> <one-to-many class="TreeItem" /> </set> </class> </hibernate-mapping>
What is interesting in the mapping is that I have set a cascade.
cascade="all,delete-orphan"
This is convenient in a twofold sense: First, when you create a tree, you only have to save the top item. All other items will be saved automatically. Second, when you delete an item the whole subtree will be deleted. The created table is:
CREATE TABLE ttreeitem ( id int4 NOT NULL, name varchar(255), parent_id int4, PRIMARY KEY (id), FOREIGN KEY (parent_id) REFERENCES ttreeitem (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) ;
Samples of use:
/* create and set relationship */ TreeItem main = new TreeItem(null, "main"); TreeItem sub1 = new TreeItem(null, "go swimming"); sub1.setParent(main); main.getChildren().add(sub1); TreeItem sub11 = new TreeItem(null, "lake"); sub11.setParent(sub1); sub1.getChildren().add(sub11); session.save(main); // cascade will save all the children /* delete a sub tree */ // reattach subTree to the new session using lock session.buildLockRequest(LockOptions.NONE).lock(sub1); /* remove the children from the parent or it will be resaved when the parent is saved. */ sub1.getParent().getChildren().remove(sub1); session.delete(sub1);
Warning: You can create recursive relations with bag or set notation. If you want to use other like an indexed list, you must create some “dirty" work arounds. Have a look in the Hibernate reference for more information.