1対多関連(Hibernate3)
多対1を以前に調査したが,hibernateは逆に1対多もなんとかしてくれるようなので,調査してみた.
本頁のサンプルでは,以下のテーブルを扱う.
これは前回の複合キーのサンプルで作成したテーブルと同じである.
すでに多対1のマッピングがなされているので,これにさらに逆の1対多のマッピングを記述する.
すなわち,Outline:Detail,Detail:MoreDetailが1対多なので,OutlineからDetail,DetailからMoreDetailを引っ張れるようにする.
Detailのマッピングでは,前回までのサンプルで既にOutlineに対する多対1のマッピングがある.
1対多ということで,Outlineを参照するDetailは複数あり,よってOutlineの該当メンバは複数のDetailオブジェクトを保持する必要がある.
そこで,要素setを使用して要素one-to-menyを囲んでいる.
前回のサンプルまでと異なるのは,メンバdetailsとそのsetter,getterである.
<key-many-to-one name="outline" class="Outline" column="ID"/>主キーの一部でもあったので,many-to-oneではなくkey-many-to-oneだが,考え方はmany-to-oneと同じである. 逆に1対多のマッピングを行うには,Outlineのマッピングに要素one-to-menyを使用して記述する.
<set name="details" lazy="false"> <key column="ID"/> <one-to-many class="Detail"/> </set>Outlineオブジェクトは(java.util.)Set型のメンバdetailsがあり,その要素はOutlineと1対多で関連づくDetailオブジェクトである. その関連づけは,要素keyで示される. すなわち列IDでなされる(IDはDetailにおける外部キーである). 要素setの属性lazyについては後述する. 以上を踏まえると,OutlineのPOJOクラスは以下のようになる.
DetailとMoreDetailも1対多である.
OutlineとDetailとの違いは,関連づけの列が複数あることである.
すなわちDetailを参照するMoreDetailの外部キーが複合キーである.
Detailのマッピングファイルは以下となる.
やはり要素keyと要素one-to-manyを要素setで囲む.
<set name="moreDetails" lazy="false">
<key>
<column name="ID"/>
<column name="REGIST_DATE"/>
</key>
<one-to-many class="MoreDetail"/>
</set>
DetailとMoreDetailは二つの列IDとREGIST_DATEで関連づくので,要素keyが二つの子要素columnをもち,その要素columnで複数の関連づけのキー(IDとREGIST_DATE)を指定している.
要素one-to-manyに関しては同じ.
DetailのPOJOクラスはOutlineと同じ要領で,以下のようになる.
以上のマッピングを使用して,1対多参照を行うサンプルを作成した.
ポイントは二つのメソッド,
- referDetailsRelatedToOutline(SessionFactory sessionFactory, Outline outline)
- referMoreDetailsRelatedToDetail(SessionFactory sessionFactory, Detail detail)
- Outlineの取得
select outline0_.ID as ID, outline0_.CODE as CODE0_ from Outline outline0_
- 各Outlineについて,紐づくDetailの取得(複数回)
select details0_.ID as ID__, details0_.REGIST_DATE as REGIST2___, details0_.ID as ID0_, details0_.REGIST_DATE as REGIST2_0_ from Detail details0_ where details0_.ID=?
- Detailの取得
select detail0_.ID as ID, detail0_.REGIST_DATE as REGIST2_ from Detail detail0_
- 各Detailについて,紐づくMoreDetailの取得(複数回)
select moredetail0_.ID as ID__, moredetail0_.REGIST_DATE as REGIST2___, moredetail0_.SEQ_NO as SEQ3___, moredetail0_.ID as ID0_, moredetail0_.REGIST_DATE as REGIST2_0_, moredetail0_.SEQ_NO as SEQ3_0_, moredetail0_.VALUE as VALUE2_0_ from MoreDetail moredetail0_ where moredetail0_.ID=? and moredetail0_.REGIST_DATE=?
サンプルには注意点がある. サンプルは以下の処理手順となっている.- Outlineの生成
- Outlineの読み込み
- Detailの生成
- 読み込んでおいたOutlineから紐づくDetailを見る→0件
- Outlineを再度読み込み
- 読み込んでおいたOutlineから紐づくDetailを見る→複数件
- DetailとMoreDetailについても同様
2005-04-22 00:09:27,649 ERROR [main] hibernate.LazyInitializationException:19 - failed to lazily initialize a collection (org.fireproject.hibernatesample.Outline.details) - no session or session was closed org.hibernate.LazyInitializationException: failed to lazily initialize a collection (org.fireproject.hibernatesample.Outline.details) - no session or session was closed at org.hibernate.collection.AbstractPersistentCollection.initialize (AbstractPersistentCollection.java:179) at org.hibernate.collection.AbstractPersistentCollection.read (AbstractPersistentCollection.java:47) at org.hibernate.collection.PersistentSet.size (PersistentSet.java:109) at org.fireproject.hibernatesample.CompositKey.referDetailsRelatedToOutline (CompositKey.java:168) at org.fireproject.hibernatesample.CompositKey.main (CompositKey.java:35)
本家の記述によると,lazyの値をtrueにして動作させるには,classファイルに対して何かしなければならない. サンプルでも$ ant $ ant instrument $ java -jar hibernatesample.jar
としたらlazyの値がtrueでも例外が発生せずに動作した. しかし,getDetailsやgetMoreDetails時に勝手にSQL発行という動きにはなっていない... うーん.
※ lazy ... 怠惰な

