Hibernate 入門記 コレクションその5 map で element で index

今回からはマップの学習です.
前回まで学習したセットでは,関連先のオブジェクトを参照するために iterator を使って一つずつたどるしかありませんでした.セットっていうのはそういうものなので当然なのですが,たいていの場合これでは不便です.この入門記では全然不便に感じていませんが,そんなのはまれでしょう.
そこでマップの登場です.ところで「Map房」って何ですか?


ともあれ,マップのマッピング(うわー)に使うのが,

  • <map> 要素

です.
使い方は限りなく <set> 要素と同じです.属性は全く同じ.
唯一違うのは,マップのキーのマッピングを指定するために,次のいずれかを子要素として記述することです.

  • <index>
  • <composite-index>
  • <index-many-to-many>
  • <index-many-to-any>

これらのうちのいずれかを,<key> 要素の後に記述します.
今回はまず,<index> 要素を見ていくことにしましょう.


マップなのにインデックスとはどういうことよ? と思ったりもするのですが,<key> 要素はすでに使用済みだし,リスト(順序付きコレクション)のインデックスと同じ要素を使って指定するためにこうなっているみたいです.
この <index> 要素は,次の属性を持っています.

column
マップのキーとなるカラムを指定します.
type
マップのキーの型を指定します.

リファレンスによると,いずれも必須とのことです.DTD的にはいずれも任意なのですが,これはリストでは type 属性は不要だったりするからみたい.
いずれにせよ簡単に使えそうです.


早速お試ししましょう.
ちょうど前回お試ししたCMテーブルが企業名と製品名という二つのカラムを持っているので,これをキーと値として使いましょう.
ただし,前回優ちゃんの出演CMとしてカネボウの2製品を使ったりしたわけで,この場合の企業名はマップのキーとして使うにはかなり不適切です.そのあたりは心より恥じるということでご容赦を.
ということで,テーブル定義は前回と同じです.
そしてモデルの永続クラス.SetMap に変更しています.

package study;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import net.sf.hibernate.CallbackException;
import net.sf.hibernate.Lifecycle;
import net.sf.hibernate.Session;

public class Model implements Lifecycle {
    int id = -1;
    String name;
    Map cm;

    public Model() {
    }
    public Model(String name) {
        this.name = name;
        this.cm = new HashMap();
    }

    public String toString() {
        return name + ", " + cm;
    }

    public void onLoad(Session s, Serializable id) {
        System.out.println("onLoad() : " + toString());
    }
    public boolean onSave(Session s) throws CallbackException {
        System.out.println("onSave() : " + toString());
        return false;
    }
    public boolean onUpdate(Session s) throws CallbackException {
        System.out.println("onUpdate() : " + toString());
        return false;
    }
    public boolean onDelete(Session s) throws CallbackException {
        System.out.println("onDelete() : " + toString());
        return false;
    }
}

そしてモデルのマッピングファイル.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
<hibernate-mapping auto-import="false" package="study">
    <class name="Model">
        <id name="id" access="field" unsaved-value="-1">
            <generator class="identity"/>
        </id>

        <property name="name" access="field"/>
        <map name="cm" access="field" cascade="all">
            <key column="model"/>
            <index column="corporation" type="string"/>
            <element column="product" type="string"/>
        </map>
    </class>
</hibernate-mapping>

<set> 要素を <map> 要素に変更して,<index> 要素を追加しています.その column 属性は当然企業名です.
コレクションの要素はCMの製品名だけになるので,<composite-element> ではなく,<element> に変更しています.
hibernate.cfg.xml は前回と同じ.
そして実行用のクラスです.

package study;
import java.util.Iterator;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cfg.Configuration;

public class Main {
    public static void main(String[] args) {
        try {
            Configuration config = new Configuration();
            SessionFactory factory = config.configure().buildSessionFactory();

            Session session = factory.openSession();

            Model yuri = new Model("Yuri Ebihara");
            yuri.cm.put("アビバ", "パソコン教室");
            yuri.cm.put("ゼスプリ", "ゴールドキウイ");
            session.save(yuri);

            Model sayo = new Model("Sayo Aizawa");
            sayo.cm.put("am/pm", "お弁当");
            sayo.cm.put("マルイ", "水着");
            session.save(sayo);

            Model yu = new Model("Yu Yamada");
            yu.cm.put("カネボウ", "テスティモ");
            yu.cm.put("ボーダフォン", "携帯");
            session.save(yu);

            session.flush();

            session = factory.openSession();
            Iterator it = session.find("from study.Model").iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }
        }
        catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

キーが重複する都合上,優ちゃんのアリィーを削っちゃいました.無念だ.
こいつを実行!!

 onSave() : Yuri Ebihara, {ゼスプリ=ゴールドキウイ, アビバ=パソコン教室}
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : Sayo Aizawa, {マルイ=水着, am/pm=お弁当}
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : Yu Yamada, {カネボウ=テスティモ, ボーダフォン=携帯}
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 Hibernate: insert into cm (model, corporation, product) values (?, ?, ?)
 Hibernate: select model0_.id as id, model0_.name as name from Model model0_
 Hibernate: select cm0_.product as product__, cm0_.model as model__, cm0_.corporation as corporat3___ from cm cm0_ where cm0_.model=?
 onLoad() : Yuri Ebihara, {ゼスプリ=ゴールドキウイ, アビバ=パソコン教室}
 Hibernate: select cm0_.product as product__, cm0_.model as model__, cm0_.corporation as corporat3___ from cm cm0_ where cm0_.model=?
 onLoad() : Sayo Aizawa, {マルイ=水着, am/pm=お弁当}
 Hibernate: select cm0_.product as product__, cm0_.model as model__, cm0_.corporation as corporat3___ from cm cm0_ where cm0_.model=?
 onLoad() : Yu Yamada, {カネボウ=テスティモ, ボーダフォン=携帯}
 Yuri Ebihara, {ゼスプリ=ゴールドキウイ, アビバ=パソコン教室}
 Sayo Aizawa, {マルイ=水着, am/pm=お弁当}
 Yu Yamada, {カネボウ=テスティモ, ボーダフォン=携帯}

やったね!!
それにしてもなんですなぁ,最近の Hibernate 入門記は地道というかルーチンワークみたいになってるなぁ.無念だ.