読者です 読者をやめる 読者になる 読者になる

Composite Pattern

翻訳

http://www.hibernate.org/86.html

hibernateでコンポジットを実装してみます。

http://c2.com/cgi/wiki?CompositePattern

hibernate annotationを使用した例については、http://www.theresearchkitchen.com/blog/archives/57
のエントリを参照してください。

マッピングファイルは下記の通り。













net.sf.hibernate.tool.hbm2java.CodeGeneratorを使って、mysqlのテーブルを生成します。

mysql> desc site;

                                                                                                                                        • +
Field Type Null Key Default Extra
                                                                                                                                        • +
id int(11) PRI NULL auto_increment
version bigint(20) 0
parent int(11) YES MUL NULL
description varchar(64)
namespace varchar(64)
name varchar(64)
                                                                                                                                        • +

6 rows in set (0.03 sec)

下記のコードで、getChildrenSites()とgetParent()が使用できます。

List sites = session.find("FROM Site AS site");
System.out.println("Number of sites returned: " + sites.size());
Iterator iter = sites.iterator();
while (iter.hasNext()) {
Site site = (Site) iter.next();
site.getName();
site.getChildrenSites();
site.getParent();
}

Implementing the Composite design pattern with Hibernate Annotations(annotationベースのエントリの翻訳)

http://www.theresearchkitchen.com/blog/archives/57

アプリケーションを開発しているときは非常に頻繁に、エンティティの階層構造を構築する局面があるかと思います。例えば、組織の階層構造とか。これは、コンポジットパターンを使うことにより、エレガントに表現することができます。ですが、永続対象となるオブジェクトをツリー表現する場合は、非常に複雑なことになってしまいます。ここでは、hibernate3とannotationのパッケージを使用して、どのように実現することができるか示します。

表現したい階層構造は、下記のようなものです。

まずは、コンポジットの親となるクラスを定義します。

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 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 getChildren() {
return children;
}
public void setChildren(Set 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;
}
}

次は、リーフとなるエンティティです。

@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;
}
}

同じレベルでリーフとなるエンティティも定義します。

import javax.persistence.Entity;
import javax.persistence.Inheritance;
@Entity()
@Inheritance(discriminatorValue="B")
public class BusinessArea extends OrganisationUnit {
}

オブジェクトツリーを作成するテストケースも書きます。

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 children = new HashSet();
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);
}
}
}

ここで使用しているPersistentTestCaseは、Junitのテストケースを拡張し、springのOpen Session In Viewの機能を付加したものです。