Hibernate 入門記 コレクションその8 array で many-to-many

今回は配列です.配列はリストと同じく順序付きコレクションです.
配列のマッピングには,

  • <array> 要素

を使います.使い方はほとんど <list> 要素と同じです.
まず,属性については lazy を指定することができないようです.代わりに次の属性が増えています.

element-class
リファレンスに解説されていないのでよくわかりませんが,配列の要素の型でしょう.任意の属性なので,デフォルトはプロパティの要素の型になると思われます.

<array> 要素の内容は <list> 要素と全く同じで,<key> 要素,<index> 要素に続いて <element> 要素・<composite-element> 要素・<one-to-many> 要素・<many-to-many> 要素・<many-to-any> 要素のいずれかを記述します.いずれもすでに学習済みです.おっと,<many-to-any> は常にスキップです.心より恥じる.
以上.相変わらず手抜きが続いているようにしか見えませんね... このシリーズを続ける意味はあるのだろうか? いや,一通り試すことに意義があるのだ.たぶん.きっと.


ということで一応お試しします.
基本的に前回のリストと同じでもいいわけですが,あまりにコードや結果のログが長くてごめんなさいだったので,今回はもっとシンプルにということで,いつものモデルと雑誌でいきます.
まずはテーブル.

CREATE TABLE MODEL (
    ID INTEGER IDENTITY,
    NAME VARCHAR
)

CREATE TABLE MAGAZINE (
    ID INTEGER IDENTITY,
    NAME VARCHAR
)

CREATE TABLE MODEL_MAGAZINE (
    ID INTEGER IDENTITY,
    INDEX INTEGER,
    MODEL INTEGER,
    MAGAZINE INTEGER
)

そしてモデルのクラス.

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;
    Magazine[] magazine;

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

    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append(name).append("[");
        for (int i = 0; magazine != null && i < magazine.length; ++i) {
            buf.append(magazine[i]).append(", ");
        }
        buf.setLength(buf.length() - 2);
        buf.append("]");
        return new String(buf);
    }

    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"/>
        <array name="magazine" access="field" table="MODEL_MAGAZINE" cascade="all">
            <key column="model"/>
            <index column="index"/>
            <many-to-many column="magazine" class="Magazine"/>
        </array>
    </class>
</hibernate-mapping>

今回は多対多で初めて cascade 属性に "all" を設定してみました.
次に雑誌のクラス.

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

public class Magazine implements Lifecycle {
    int id = -1;
    String name;

    public Magazine() {
    }
    public Magazine(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;
    }
}

そして雑誌のマッピングファイル.

<?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="Magazine">
        <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();

            Magazine cancam = new Magazine("CanCam");
            Magazine vivi = new Magazine("ViVI");
            Magazine classy = new Magazine("CLASSY");

            Model yuri = new Model("Yuri Ebihara");
            yuri.magazine = new Magazine[1];
            yuri.magazine[0] = cancam;
            session.save(yuri);

            Model sayo = new Model("Sayo Aizawa");
            sayo.magazine = new Magazine[2];
            sayo.magazine[0] = vivi;
            sayo.magazine[1] = classy;
            session.save(sayo);

            Model asami = new Model("Asami Usuda");
            asami.magazine = new Magazine[1];
            asami.magazine[0] = cancam;
            session.save(asami);

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

明示的に Session#save(Object) を呼び出しているのはモデルだけです.
こいつを実行!

 onSave() : Yuri Ebihara[CanCam]
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : CanCam
 Hibernate: insert into Magazine (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : Sayo Aizawa[ViVI, CLASSY]
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : ViVI
 Hibernate: insert into Magazine (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : CLASSY
 Hibernate: insert into Magazine (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 onSave() : Asami Usuda[CanCam]
 Hibernate: insert into Model (name, id) values (?, null)
 Hibernate: CALL IDENTITY()
 Hibernate: insert into MODEL_MAGAZINE (model, index, magazine) values (?, ?, ?)
 Hibernate: select model0_.id as id, model0_.name as name from Model model0_
 Hibernate: select magazine0_.magazine as magazine__, magazine0_.model as model__, magazine0_.index as index__ from MODEL_MAGAZINE magazine0_ where magazine0_.model=?
 Hibernate: select magazine0_.id as id0_, magazine0_.name as name0_ from Magazine magazine0_ where magazine0_.id=?
 onLoad() : CanCam
 onLoad() : Yuri Ebihara[CanCam]
 Hibernate: select magazine0_.magazine as magazine__, magazine0_.model as model__, magazine0_.index as index__ from MODEL_MAGAZINE magazine0_ where magazine0_.model=?
 Hibernate: select magazine0_.id as id0_, magazine0_.name as name0_ from Magazine magazine0_ where magazine0_.id=?
 onLoad() : ViVI
 Hibernate: select magazine0_.id as id0_, magazine0_.name as name0_ from Magazine magazine0_ where magazine0_.id=?
 onLoad() : CLASSY
 onLoad() : Sayo Aizawa[ViVI, CLASSY]
 Hibernate: select magazine0_.magazine as magazine__, magazine0_.model as model__, magazine0_.index as index__ from MODEL_MAGAZINE magazine0_ where magazine0_.model=?
 onLoad() : Asami Usuda[CanCam]
 Yuri Ebihara[CanCam]
 Sayo Aizawa[ViVI, CLASSY]
 Asami Usuda[CanCam]

ふむ.特にどうということもなく,いつもと同じっぽい.
カスケード周りが気になったのであれこれ試してみたのですが,カスケード削除の方はあまりうまくいっていないような?
参照されなくなった雑誌の削除まではしてくれませんでした.まぁそんなもんだろうと予想はしていましたが.


これで配列も終了ということにします.残すところはバッグの二つのみ.今週中にはコレクションシリーズを卒業できるかな? いや,金曜日は EbiYuri デーだから難しいか? 心より恥じる.