リレーショナルデータベースの特徴は、その名のとおりデータを「リレーショナル」に扱えることにあります。リレーショナルに扱えるデータとはID番号や名前などのキーとなるフィールドのデータを利用して、「結合」や「抽出」を行えるデータです。
O/Rマッピングフレームワークの良しあしを判断するうえで重要な点は、リレーショナルデータベースの強力な機能を殺さず、かつ扱いやすいAPI(アプリケーション・プログラミング・インターフェイス)を提供しているか否かという点です。今回はHibernateを使ったリレーショナルデータの表現方法を紹介します。
リレーショナルデータの準備と実行
今回のサンプルでは、テーブルを以下のように作成します。WorkGroupの1レコードに複数のMemberレコードが所属するという「1対多」のリレーションを持つテーブル構成となっています。使用するテーブルのER図は以下のようになります。
また、テーブルは以下のように作成します。
列名 |
データ型 |
主キー |
Not Null |
NO |
INTEGER |
○ |
○ |
NAME |
VARCHAR(10) |
|
GROUPNO |
INTEGER |
|
|
表1 Memberテーブルの定義 |
列名 |
データ型 |
主キー |
Not Null |
GROUPNO |
INTEGER |
○ |
○ |
GROUPNAME |
VARCHAR(10) |
|
|
表2 WorkGroupテーブルの定義 |
作成したテーブルには、サンプルとして以下のようなテストデータを登録しておきます。
NO |
NAME |
GROUPNO |
1 |
iwashita |
1 |
2 |
kawazu |
1 |
3 |
kawano |
2 |
4 |
Uozaki |
1 |
5 |
masuda |
1 |
6 |
mitsuyoshi |
2 |
7 |
taga |
1 |
8 |
tanaka |
2 |
9 |
tanizawa |
1 |
10 |
yamamoto |
2 |
11 |
ysohida |
1 |
表3 Memberテーブルのサンプルデータ |
GROUPNO |
GROUPNAME |
1 |
tokyo |
2 |
osaka |
表4 WorkGroupテーブルのサンプルデータ |
マッピングファイルへの記述
テーブルが用意できたらマッピングファイルおよび永続化のためのJavaコードを作成していきましょう。まずは、Memberテーブルに対応するマッピングファイルを記述しましょう。
リスト1 Member.hbm.xmlファイル |
<?xml version="1.0" ?>
<!DOCTYPE hibernate-mapping PUBLIC "
-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/
hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="sample.entity.Member" table="MEMBER">
// 1.主キーカラム |
<id name="no" column="NO" type="java.lang.Integer" >
<generator class="assigned" />
</id> |
<property name="name" type="string" column="NAME" />
<property name="groupno" type="java.lang.Integer"
column="GROUPNO" />
// 2.「多対1」マッピングカラム |
<many-to-one name ="workGroup" column="GROUPNO"
class="sample.entity.WorkGroup" cascade="all" outer-join="auto"
update="false" insert="false" /> |
</class>
</hibernate-mapping> |
ここで、リスト1の中のコメント部分に関して少し詳細に解説しておきます。
1. 主キーカラム |
<id name="no" column="NO" type="java.lang.Integer" >
<generator class="assigned" />
</id> |
マッピングテーブルには主キーが必要です。Hibernateには永続化時に主キーの「値」を生成するためのIDジェネレータークラスが用意されています。サンプルでは「assigned」を使用しています。このIDジェネレーターは主キーの自動生成を行いません。よって、アプリケーション側で一意なIDを設定する必要があります。IDジェネレーターの一部を以下に紹介します。
名前 |
説明 |
Increment |
インクリメンタルなLong、Short、INT型のIDを生成します |
hilo |
hi/loアルゴリズムを使用してIDを生成します |
uuid.hex |
128ビットのUUIDアルゴリズムを使用して文字列型のIDを生成しますuuid.string |
uuid.string |
uuid.hexと同じアルゴリズムを使い、16けたのASCII文字でエンコードされたUUIDを生成します |
native |
使用するデータベースの機能に応じて、identity、sequence、またはhiloを選びます |
assigned |
アプリケーションでIDを生成し、永続化される前にオブジェクトに割り当てる必要があります |
foreign |
1対1のリレーションが設定されているときに、関連先のオブジェクトのIDを使用します |
表5 IDジェネレーター |
このほかにも多くのIDジェネレーターが提供されています。詳しい使用方法などはHibernateのマニュアルを参照してください。
2. 「多対1」マッピングカラム |
<many-to-one name ="workGroup" column="GROUPNO"
class="sample.entity.WorkGroup" cascade="all" outer-join="auto"
update="false" insert="false" /> |
MemberテーブルからWorkGroupテーブルへのマッピングを表現するタグとして<many-to-one>タグを使用します。
データベース設計に堪能な方なら「1対多」のリレーションはよくご存じでしょうが、「多対1」という呼び方にはなじみがないかもしれません。オブジェクト指向での「1対多」のリレーションは、そのリレーションが発生する主体によって「1対多」と「多対1」の2つの側面でとらえることができます。
サンプルで使用しているリレーションを、Memberテーブルを主体としてリレーションを見てみると、Memberテーブルのデータ複数に対してWorkGroupテーブルのデータが1つ決まるため「多対1」ということになりmany-to-oneのリレーションが使用されます。
表6 Member表からWorkGroup表へのリレーション
NO |
NAME |
GROUPNO |
0...1 |
GROUPNO |
GROUPNAME |
1 |
iwashita |
1 |
 |
1 |
tokyo |
2 |
kawazu |
1 |
|
4 |
uozaki |
1 |
5 |
matsuda |
1 |
7 |
taga |
1 |
8 |
tanizawa |
1 |
11 |
yoshida |
1 |
|
続いて、WorkGroupテーブルに対応するマッピングファイルを記述します。
リスト2 WorkGroup.hbm.xmlファイル |
<?xml version="1.0" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="sample.entity.WorkGroup" table="WORKGROUP">
<id name="groupno" column="GROUPNO" type="java.lang.Integer" >
<generator class="assigned" />
</id>
<property name="groupname" type="string" column="GROUPNAME" />
// 3.「1対多」マッピングカラム |
<set name="memberSet" >
<key ><column name="groupno" /></key>
<one-to-many class="sample.entity.Member" />
</set> |
</class>
</hibernate-mapping> |
「1対多」マッピングカラム
WorkGroupテーブルからMemberテーブルへの「1対多」のリレーションでは、複数レコードを扱う必要があるためコレクション型の指定が必要です。このサンプルではjava.util.Setインターフェイス型を用いてマッピングします。サンプルで使用している<set>タグのほかにも<map> <list> <bag>などがあります。
<set name="memberSet" >
<key ><column name="groupno" /></key>
<one-to-many class="sample.entity.Member" />
</set> |
Entity Javaコードの記述
次にJavaコードの中で使用する永続化クラスを作成します。これらのクラスは、EntityBeanの重厚なオブジェクトに対して、シンプルな普通のオブジェクトという意味を強調してPOJO(Plain Old Java Object)と呼ばれています。
リスト3 Memberクラスのコード |
package sample.entity;
import java.io.Serializable;
public class Member implements Serializable {
private Integer no;
private String name;
private Integer groupno;
private WorkGroup workGroup; //リレーション先の型で定義しているところに注目
public Member() { }
public Integer getGroupno() { return groupno; }
public String getName() { return name; }
public Integer getNo() { return no; }
public WorkGroup getWorkGroup() { return workGroup; }
public void setGroupno(Integer integer) {groupno = integer; }
public void setName(String string) { name = string; }
public void setNo(Integer integer) { no = integer; }
public void setWorkGroup(WorkGroup group) {workGroup = group;}
} |
リスト4 WorkGroupクラスのコード |
package sample.entity;
import java.util.Set;
public class WorkGroup {
private Integer groupno;
private String groupname;
private Set memberList;
//マッピングファイルに指定したコレクション型で定義している
public WorkGroup(){ }
public String getGroupname() { return groupname; }
public Integer getGroupno() { return groupno; }
public Set getMemberSet() { return memberList; }
public void setGroupname(String string) { groupname = string; }
public void setGroupno(Integer integer) { groupno = integer; }
public void setMemberSet(Set set) { memberList = set; }
} |
サンプル実行Javaコードの記述
さて、準備が整ったのでサンプルを実行するJavaコードを記述していきましょう。
リスト5 サンプル実行Javaコード |
3package sample;
import java.util.*;
import sample.entity.*;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.Configuration;
public class WorkgroupFound {
public static void main(String args[]) throws Exception{
Configuration config = new Configuration().configure(); // 設定の読み込み
// セッションファクトリーの作成
SessionFactory sessionfactory = config.buildSessionFactory();
// セッションオープン
Session session = sessionfactory.openSession();
// Memberテーブルの全件検索
List list = session.find(" FROM WorkGroup ");
//検索処理の実行
for(int i=0;i < list.size();i++){
WorkGroup group = (WorkGroup)list.get(i);
System.out.println(group.getGroupno() + ":" + group.getGroupname());
//コンソールへの書き出し
Iterator memberList =group.getMemberSet().iterator();
//リレーション先オブジェクトの取り出し
while (memberList.hasNext()) {
Member member = (Member)memberList.next();
// NOとNAME列のデータを表示
System.out.println("\t" + member.getNo() + ":" + member.getName());
//コンソールへの書き出し
}
}
}
} |
プログラムを実行すると、以下のような出力情報がコンソールに表示されます。
サンプルの実行結果
Sessionインターフェイスのfind()メソッドに対して、SQLに似た文字列のクエリを指定することで検索結果が得られます。このクエリをHQL(Hibernate Query Language)と呼びます。このHQLによってWorkGroupにひも付くMemberのリストが得られました。この取得結果をイメージで表すと以下のようになります。
Copyright © ITmedia, Inc. All Rights Reserved.