連載
» 2004年09月22日 00時00分 UPDATE

Hibernateで理解するO/Rマッピング(5):SQLに似たHQLでリレーショナルデータを柔軟に検索 (1/2)

O/Rマッピングは、従来の煩雑なデータベースに関する処理の記述をスマートにし、柔軟なアプリケーションの構築を可能にします。本連載ではオープンソースのO/Rマッピングフレームワーク「Hibernate」を用いてO/Rマッピングの基礎を解説します。そしてさらに、J2EEアプリケーションへの実践的な適用方法とそのメリットも紹介していきます。(編集局)

[山本大,株式会社クロノス]

 リレーショナルデータベースの特徴は、その名のとおりデータを「リレーショナル」に扱えることにあります。リレーショナルに扱えるデータとはID番号や名前などのキーとなるフィールドのデータを利用して、「結合」や「抽出」を行えるデータです。

 O/Rマッピングフレームワークの良しあしを判断するうえで重要な点は、リレーショナルデータベースの強力な機能を殺さず、かつ扱いやすいAPI(アプリケーション・プログラミング・インターフェイス)を提供しているか否かという点です。今回はHibernateを使ったリレーショナルデータの表現方法を紹介します。

リレーショナルデータの準備と実行

 今回のサンプルでは、テーブルを以下のように作成します。WorkGroupの1レコードに複数のMemberレコードが所属するという「1対多」のリレーションを持つテーブル構成となっています。使用するテーブルのER図は以下のようになります。

r5fig1.gif

 また、テーブルは以下のように作成します。

列名 データ型 主キー 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のリストが得られました。この取得結果をイメージで表すと以下のようになります。

r5fig3.gif
       1|2 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。