Hibernate 入門記 継承その4 any 要素
前回までに学習した継承マッピングのうち,最後に学習した table per concrete class を使った場合には,その永続クラスへの関連に大きな制限があるようです.
まずは,「8.2. Limitations」より,各継承マッピングと扱うことのできる関連を見てみましょう.
マッピング方式 | 1対1 | 1対多 | 多対1 | 多対多 |
---|---|---|---|---|
table per class hierarchy | <one-to-one> | <one-to-many> | <many-to-one> | <many-to-many> |
table per subclass | ||||
table per concrete class | 未サポート | 未サポート | <any> | <many-to-any> |
この表は,ある永続クラス (関連元) が,継承を使った永続クラス(関連先)への関連を持つ場合に使う要素を示しています.
最初の二つのマッピング方式を使った場合は,これまで学習したとおりに関連を扱うことができます.
しかし,table per concrete class の場合には,1対1および1対多の関連はサポートされません.残念!!!!
多対1および多対多の関連を扱うことは出来ますが,そのために使う要素はこれまでスキップしまくってきたものです.よって、切腹!!!!
ということで,今回は手始めに多対1の関連について学習します.情報源は主に「5.2.5. Any type mappings」です.
まずはテーブルについて.
多対1の関連ということは,多の側つまり関連元のテーブルに外部キーがあります.
関連先は table per concrete class を使っているので,外部キーが指すのは複数のテーブルのいずれかの行です.
このため関連元のテーブルには,外部キーに加えてその外部キーが指すテーブルを識別するカラムが必要になります.
このカラムのことを... なんと呼ぶのかな? 名前が見あたらない... (;_;)
ともかく,カラムが必要です.カラムの型や値は自由に選べるようです.
次にマッピングについて.
関連元では,table per concrete class な永続クラスへの多対1の関連を
<any>
要素
を使ってマッピングします.
この要素は次の属性を持っています.
name
- プロパティの名前を指定します.必須です.
id-type
- 外部キーの型を指定します.必須です.
meta-type
- 関連先のクラスを示すカラムの型を指定します.
省略すると Hibernate 型のclass
になります. cascade
- カスケード更新を指定する場合に指定します.
省略するとnone
になります. access
- Hibernate がプロパティにアクセスする方法を指定します.
省略するとproperty
になります.
問題のカラムの型を meta-type
属性で指定します.
デフォルトの class
を指定した場合は,問題のカラムの値は永続クラスの完全限定名となります.
その他の型を指定した場合は,その値と永続クラスの型への対応を <any>
要素の子要素である
<meta-value>
要素
で指定します.
この要素は,次の属性を持っています.
value
- 問題のカラムの値.必須です.
class
- 問題のカラムの値が
value
属性の値と一致した場合の永続クラス.
さて,問題のカラムについて,型の指定方法は分かりましたが,カラム名はどうやって指定するのでしょうか? なんかよく分からない... (;_;)
うーみゅ,DTD を見ると,<any>
要素の内容モデルは次のようになっています.
<!ELEMENT any (meta*,meta-value*,column,column+)>
むむぅ,<column>
要素が一つあって,その後にまた1つ以上の <column>
要素が続く...
もしや,最初のカラムが問題のカラム,その後続くのが外部キーのカラムか!?
うー,そういわれてみれば「5.2.5. Any type mappings」の例でも最初のカラムの名前が table_name
とかってそれっぽい例になってるなぁ.よし,そういうことにしましょう.
ということでお試しです.
table per concrete class な継承を使ったところはこれまで通り.
それへの多対1関連を持つクラスとしては... んー,困ったな.何にしようかなぁ?
いいや,CM でいきます.
...センスゼロ。
そのテーブルですが,タレント・モデル・女優は前回と同じです.
CMのテーブルは次の通り.
CREATE TABLE CM ( ID INTEGER IDENTITY PRIMARY KEY, NAME VARCHAR, TALENT INTEGER, TALENT_TYPE VARCHAR )
TALENT
がタレントへの外部キー,TALENT_TYPE
がタレントの型を示すカラムです.
次に永続クラスですが,タレント・モデル・女優については前回というか「継承その1」から変わっていません.
CM の永続クラスは次の通り.
package study; import java.io.Serializable; import net.sf.hibernate.CallbackException; import net.sf.hibernate.Lifecycle; import net.sf.hibernate.Session; public class Cm implements Lifecycle { int id = -1; String name; Talent talent; public Cm() { } public Cm(String name, Talent talent) { this.name = name; this.talent = talent; } public String toString() { return name + " : " + talent; } public void onLoad(Session s, Serializable id) { System.out.println("onLoad() : " + name); } public boolean onSave(Session s) throws CallbackException { System.out.println("onSave() : " + name); return false; } public boolean onUpdate(Session s) throws CallbackException { System.out.println("onUpdate() : " + name); return false; } public boolean onDelete(Session s) throws CallbackException { System.out.println("onDelete() : " + name); return false; } }
マッピングファイルもタレント・モデル・女優は前回と同じです.
そして CM のマッピングファイル,study/Cm.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="Cm"> <id name="id" access="field" unsaved-value="-1"> <generator class="identity"/> </id> <property name="name" access="field"/> <any name="talent" id-type="integer" meta-type="string" access="field" cascade="save-update"> <meta-value value="T" class="Talent"/> <meta-value value="M" class="Model"/> <meta-value value="A" class="Actress"/> <column name="talent_type"/> <column name="talent"/> </any> </class> </hibernate-mapping>
このように,CM
テーブルの TALENT_TYPE
の値と実際の型を指定しています.
また,カスケード更新を使うようにしました.でも,対多関連では当てにならない削除はなしで♪
4つのマッピングファイルを全て,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", "CanCam"); Actress akiko = new Actress("Akiko Yada", "My Little Chef"); Cm aviva = new Cm("Aviva", yuri); session.save(aviva); Cm zespri = new Cm("Zespri", yuri); session.save(zespri); Cm aflac = new Cm("AFLAC", akiko); session.save(aflac); session.flush(); session.connection().commit(); session = factory.openSession(); Iterator it = session.find("from study.Cm").iterator(); while (it.hasNext()) { System.out.println(it.next()); } } catch (Throwable e) { e.printStackTrace(); } } }
ウッチーが CM にでてるのは知らない (民放のオリンピック共同 CM に出るとか聞いたような?) ので,エビちゃんとアッコちゃんだけにしてしまいました.残念!!!!
…イヤべつに残念でもなんでもないのですが、
こいつを実行!!!!
onSave() : Aviva onSave() : Yuri Ebihara Hibernate: insert into Model (name, magazine, id) values (?, ?, null) Hibernate: CALL IDENTITY() Hibernate: insert into Cm (name, talent_type, talent, id) values (?, ?, ?, null) Hibernate: CALL IDENTITY() onSave() : Zespri Hibernate: insert into Cm (name, talent_type, talent, id) values (?, ?, ?, null) Hibernate: CALL IDENTITY() onSave() : AFLAC onSave() : Akiko Yada Hibernate: insert into Actress (name, drama, id) values (?, ?, null) Hibernate: CALL IDENTITY() Hibernate: insert into Cm (name, talent_type, talent, id) values (?, ?, ?, null) Hibernate: CALL IDENTITY() Hibernate: select cm0_.id as id, cm0_.name as name, cm0_.talent_type as talent_t3_, cm0_.talent as talent from Cm cm0_ Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ from Model model0_ where model0_.id=? onLoad() : Yuri Ebihara onLoad() : Aviva onLoad() : Zespri Hibernate: select actress0_.id as id0_, actress0_.name as name0_, actress0_.drama as drama0_ from Actress actress0_ where actress0_.id=? onLoad() : Akiko Yada onLoad() : AFLAC Aviva : Yuri Ebihara : CanCam Zespri : Yuri Ebihara : CanCam AFLAC : Akiko Yada : My Little Chef
おおぉぉぉぉっ,でけたっぽい.最近本当に吐血が減ったなぁ〜.
実行後に CM のテーブルを確認すると,TALENT_TYPE
カラムには 'M'
や 'A'
がちゃんと入っていました.
ここで CM のマッピングファイルを次のように変更して...
<any name="talent" id-type="integer" access="field" cascade="save-update"> <column name="talent_type"/> <column name="talent"/> </any>
これで実行すると,TALENT_TYPE
カラムにはモデルクラスや女優クラスの完全限定名が入りました.
ということで,<any>
は終了です.次回はきっと <many-to-any>
です.きっと来週です.
そして EJB 3.0 の方は今日もお休みです... 申し訳ないっ!
いや〜見捨てないでくださいね〜(*_*)