Quite frequently when building applications, you will come across the requirement to build a hierarchical tree structure of entities, for instance, an organisational hierarchy. This is elegantly represented by the composite design pattern. A further twist on this is creating persistent trees of objects. Here I show you how to achieve this with Hibernate 3 using the annotations package.
Here is an example of the type of structure that I am trying to represent:
First, let’s define our composite class (the parent):
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
@Entity()
@Inheritance (
strategy=InheritanceType.SINGLE_TABLE,
discriminatorType=DiscriminatorType.STRING,
discriminatorValue="0")
@DiscriminatorColumn()
public abstract class OrganisationUnit {
private Set<OrganisationUnit> children;
private OrganisationUnit parent;
private String name;
private Long id;
private Double budget;
@Transient()
public boolean isLeaf() {
return (children == null || children.size() == 0);
}
@Transient()
public boolean isRoot() {
return (parent == null);
}
@OneToMany(mappedBy="parent",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
public Set<OrganisationUnit> getChildren() {
return children;
}
public void setChildren(Set<OrganisationUnit> children) {
this.children = children;
}
@Column()
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne()
@JoinColumn()
public OrganisationUnit getParent() {
return parent;
}
public void setParent(OrganisationUnit parent) {
this.parent = parent;
}
@Id(generate=GeneratorType.AUTO)
public Long getId() {
return id;
}
protected void setId(Long id) {
this.id = id;
}
@Column( scale=2 )
public Double getBudget() {
return budget;
}
public void setBudget(Double budget) {
this.budget = budget;
}
}
And now a leaf entity:
@Entity()
@Inheritance( discriminatorValue="C")
public class CostCentre extends OrganisationUnit {
private String costCentreId;
@Column( length=16 )
public String getCostCentreId() {
return costCentreId;
}
public void setCostCentreId(String costCentreId) {
this.costCentreId = costCentreId;
}
}
And another leaf-level entity:
import javax.persistence.Entity;
import javax.persistence.Inheritance;
@Entity()
@Inheritance(discriminatorValue="B")
public class BusinessArea extends OrganisationUnit {
}
And now a test case, which we can use to build up a (small, in this case) test tree of objects:
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
public class TestOrganisationUnitCRUD extends PersistentTestCase {
public void testSaveParent() {
OrganisationUnit parent = new BusinessArea();
parent.setName("Parent");
OrganisationUnit child1 = new CostCentre();
child1.setName("Cost Centre 1");
child1.setParent(parent);
OrganisationUnit child2 = new CostCentre();
child2.setName("Cost Centre 2");
child2.setParent(parent);
// Add the children to the set
Set<OrganisationUnit> children = new HashSet<OrganisationUnit>();
children.add(child1);
children.add(child2);
parent.setChildren(children);
// Persist the children
Session session = sessionFactory.getCurrentSession();
Long id = (Long) session.save(parent);
session.flush();
session.evict(parent);
session.evict(child1);
session.evict(child2);
OrganisationUnit entity = (OrganisationUnit) session.load(BusinessArea.class, id);
assertNotNull(entity);
assertEquals(entity.getName(), parent.getName());
assertNotNull(entity.getChildren());
assertEquals(entity.getChildren().size(), 2);
for (OrganisationUnit child: entity.getChildren()) {
assertEquals(child.getClass(), CostCentre.class);
}
}
}
In this case, PersistentTestCase is just a convenience class that extends JUnit’s TestCase class and adds support for the Open Session In View functionality provided by Spring.