Hibernate 入門記 コレクションその7 list で element

今回はリストです.このリストは java.util.List と同じような意味で,順序付きコレクションのことです.
リストのマッピングを指定するには,

  • <list> 要素

を使います.
こいつの使い方は,限りなく <map> 要素と近いようです.
まず属性については,order-by 属性が使えないことが <map> 要素と異なります.順序は後述の <index> 要素で指定するためです.そのほかの属性は <map> 要素と同じです.
内容については,最初に

  • <key> 要素

を記述します.これは <set> 要素や <map> 要素と同じで,こっち(この定義をしている永続クラス)への外部キーを指定するために使います.
次にインデックスを保持するカラムの情報を

  • <index> 要素

で指定します.<map> 要素と異なり,<composite-index> 要素や <index-many-to-many> 要素を指定することはできません.
<index> 要素の属性として次のものを指定します.

column
インデックスとなるカラムの名前を指定します.
type
インデックスとなるカラムの型を指定します.デフォルトは integer です.

ここで指定するカラムの値は,0から始まる数値でなければならないとか.ふむ.リストってそういうものなんですね.てっきり結果セットの順番で java.util.List につっこんでくれるのかと思いました.そうか,それだと INSERT/UPDATE がうまくできないのか.インデックスが途中で飛ぶような場合はどうなるんだろう? うーみゅ,お試ししますか.
ともあれ,最後に関連先のマッピングを次のいずれかで指定します.

  • <element> 要素
  • <composite-element> 要素
  • <one-to-many> 要素
  • <many-to-many> 要素
  • <many-to-any> 要素

これは <set> 要素や <map> 要素と同じですね.
以上です.マップとほとんど変わらないのでラクチンっぽい.


ということでお試しです.
順序付きかぁ.エビちゃんをランク付けするってのはちょっとなんだかなぁ.
ということで,「早耳トレンドNo1」の月別登場回数でランキングしてみましょう.
まずはテーブル定義.

CREATE TABLE HAYAMIMI (
    ID INTEGER IDENTITY,
    MONTH INTEGER
)

CREATE TABLE MODEL (
    ID INTEGER IDENTITY,
    NAME VARCHAR
)

CREATE TABLE RANKING (
    ID INTEGER IDENTITY,
    HAYAMIMI INTEGER,
    RANK INTEGER,
    MODEL INTEGER
)

そして「早耳」の永続クラス.

package study;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import net.sf.hibernate.CallbackException;
import net.sf.hibernate.Lifecycle;
import net.sf.hibernate.Session;

public class Hayamimi implements Lifecycle {
    int id = -1;
    int month;
    List model;

    public Hayamimi() {
    }
    public Hayamimi(int month) {
        this.month = month;
        this.model = new ArrayList();
    }

    public String toString() {
        return "早耳トレンドNo1 " + month + "月 " + model;
    }

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

「早耳」のマッピングファイル,study/Hayamimi.hbm.xml

<?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="Hayamimi">
        <id name="id" access="field" unsaved-value="-1">
            <generator class="identity"/>
        </id>

        <property name="month" access="field"/>
        <list name="model" access="field" table="ranking">
            <key column="hayamimi"/>
            <index column="rank"/>
            <many-to-many column="model" class="Model"/>
        </list>
    </class>
</hibernate-mapping>

そしてモデルの永続クラス.

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;

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

    public String toString() {
        return name;
    }

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

モデルのマッピングファイル,study/Model.hbm.xml

<?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"/>
    </class>
</hibernate-mapping>

二つのマッピングファイルを 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 asami = new Model("あさ美");
            session.save(asami);
            Model anne = new Model("杏");
            session.save(anne);
            Model kana = new Model("香奈");
            session.save(kana);
            Model sayo = new Model("紗世");
            session.save(sayo);
            Model jun = new Model("潤");
            session.save(jun);
            Model rosa = new Model("ローサ");
            session.save(rosa);

            Hayamimi april = new Hayamimi(4);
            april.model.add(kana);
            april.model.add(asami);
            april.model.add(jun);
            april.model.add(rosa);
            april.model.add(anne);
            april.model.add(sayo);
            session.save(april);

            Hayamimi may = new Hayamimi(5);
            may.model.add(asami);
            may.model.add(kana);
            may.model.add(anne);
            may.model.add(jun);
            may.model.add(rosa);
            may.model.add(sayo);
            session.save(may);

            Hayamimi june = new Hayamimi(6);
            june.model.add(asami);
            june.model.add(kana);
            june.model.add(anne);
            june.model.add(jun);
            june.model.add(sayo);
            june.model.add(rosa);
            session.save(june);

            session.flush();

            session = factory.openSession();
            Iterator it = session.find("from study.Hayamimi").iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }

            session = factory.openSession();
            Hayamimi hayamimi = (Hayamimi) session.find("from study.Hayamimi hayamimi where hayamimi.month = 4").iterator().next();
            hayamimi.model.remove(0);
            session.update(hayamimi);
            session.flush();

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

最初にしっかりとデータを作って,後から4月のランク1位を取り除いています.どうなりますやら.
こいつを実行!!

 onSave() : あさ美
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : 杏
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : 香奈
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : 紗世
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : 潤
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : ローサ
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : 早耳トレンドNo1 4月 [香奈, あさ美, 潤, ローサ, 杏, 紗世]
 Hibernate: insert into Hayamimi (month, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : 早耳トレンドNo1 5月 [あさ美, 香奈, 杏, 潤, ローサ, 紗世]
 Hibernate: insert into Hayamimi (month, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : 早耳トレンドNo1 6月 [あさ美, 香奈, 杏, 潤, 紗世, ローサ]
 Hibernate: insert into Hayamimi (month, id) values (?, null)
 Hibernate: CALL IDENTITY()
 Hibernate: insert into ranking (hayamimi, rank, model) values (?, ?, ?)
 Hibernate: select hayamimi0_.id as id, hayamimi0_.month as month from Hayamimi hayamimi0_
 Hibernate: select model0_.model as model__, model0_.hayamimi as hayamimi__, model0_.rank as rank__ from ranking model0_ where model0_.hayamimi=?
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 香奈
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : あさ美
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 潤
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : ローサ
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 杏
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 紗世
 onLoad() : 早耳トレンドNo1 4月 [香奈, あさ美, 潤, ローサ, 杏, 紗世]
 Hibernate: select model0_.model as model__, model0_.hayamimi as hayamimi__, model0_.rank as rank__ from ranking model0_ where model0_.hayamimi=?
 onLoad() : 早耳トレンドNo1 5月 [あさ美, 香奈, 杏, 潤, ローサ, 紗世]
 Hibernate: select model0_.model as model__, model0_.hayamimi as hayamimi__, model0_.rank as rank__ from ranking model0_ where model0_.hayamimi=?
 onLoad() : 早耳トレンドNo1 6月 [あさ美, 香奈, 杏, 潤, 紗世, ローサ]
 早耳トレンドNo1 4月 [香奈, あさ美, 潤, ローサ, 杏, 紗世]
 早耳トレンドNo1 5月 [あさ美, 香奈, 杏, 潤, ローサ, 紗世]
 早耳トレンドNo1 6月 [あさ美, 香奈, 杏, 潤, 紗世, ローサ]
 Hibernate: select hayamimi0_.id as id, hayamimi0_.month as month from Hayamimi hayamimi0_ where (hayamimi0_.month=4 )
 Hibernate: select model0_.model as model__, model0_.hayamimi as hayamimi__, model0_.rank as rank__ from ranking model0_ where model0_.hayamimi=?
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 香奈
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : あさ美
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 潤
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : ローサ
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 杏
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 紗世
 onLoad() : 早耳トレンドNo1 4月 [香奈, あさ美, 潤, ローサ, 杏, 紗世]
 Hibernate: delete from ranking where hayamimi=? and rank=?
 Hibernate: update ranking set model=? where hayamimi=? and rank=?
 Hibernate: select hayamimi0_.id as id, hayamimi0_.month as month from Hayamimi hayamimi0_
 Hibernate: select model0_.model as model__, model0_.hayamimi as hayamimi__, model0_.rank as rank__ from ranking model0_ where model0_.hayamimi=?
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : あさ美
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 潤
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : ローサ
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 杏
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 紗世
 onLoad() : 早耳トレンドNo1 4月 [あさ美, 潤, ローサ, 杏, 紗世]
 Hibernate: select model0_.model as model__, model0_.hayamimi as hayamimi__, model0_.rank as rank__ from ranking model0_ where model0_.hayamimi=?
 Hibernate: select model0_.id as id0_, model0_.name as name0_ from Model model0_ where model0_.id=?
 onLoad() : 香奈
 onLoad() : 早耳トレンドNo1 5月 [あさ美, 香奈, 杏, 潤, ローサ, 紗世]
 Hibernate: select model0_.model as model__, model0_.hayamimi as hayamimi__, model0_.rank as rank__ from ranking model0_ where model0_.hayamimi=?
 onLoad() : 早耳トレンドNo1 6月 [あさ美, 香奈, 杏, 潤, 紗世, ローサ]
 早耳トレンドNo1 4月 [あさ美, 潤, ローサ, 杏, 紗世]
 早耳トレンドNo1 5月 [あさ美, 香奈, 杏, 潤, ローサ, 紗世]
 早耳トレンドNo1 6月 [あさ美, 香奈, 杏, 潤, 紗世, ローサ]

ふむふむ.
関連を取り除くと,関連テーブルに対する DELETE と UPDATE が実行されるのですね(太字になっているところ).
ちなみに,順序づけられた関連レコードの最後(ランクが一番大きい行)が削除されて,1番目から最後の1つ前(新しい最後)までの行の MODEL カラムが更新されています.
この状況を見ると,順序が頻繁に変わるような場合には使いたくない感じですね.リストの後ろに追加するだけの場合は悪くないかも.


こんなもんでリストは終了.次回は配列に挑みます.