丁度、仕事で必要になったので実装しました。
(明日リリース予定のDBFlute-0.5.6)
今まで、「業務的にone-to-one」というのがDBFluteにとっては鬼門でした。
o 会員(MEMBER)テーブル
o 会員住所情報(MEMBER_ADDRESS)テーブル
とあったとします。
ここで互いに同じ主キー値を持つなり、MemberAddressでユニーク制約
の掛かったMemberIdカラムを持つなどで、制約的にone-to-oneであれば、
全く問題ありません。DBFluteはその制約を検知して、
MemberCB member = new MemberCB();
member.setupSelect_MemberAddressAsOne();
というように実装が可能になります。
しかし、MemberAddressで「VALID_FLGが有効なもので絞ればone-to-one」
というDB構造になっている場合、そのことを知っているのは人間だけなので
DBFluteで上記のようなメソッドを作成することはできません。
つまり、構造的にはone-to-manyだが業務的にone-to-oneになるような状態。
from MEMBER mb
left outer join MEMBER_ADDRESS adr
on mb.MEMBER_ID = adr.MEMBER_ID and adr.VALID_FLG = 1
履歴を同じテーブルで扱う場合とかによく利用される手法だと思います。
自分が設計する場合は極力避けるようにしますが、仕方ない場合もあるのだと思います。
この場合だと、今までのConditionBeanでは、MemberからMemberAddressを
外部結合することができませんでした。しかも、一つそういった関係のテーブル構造が
あると、大抵プロジェクトでは他にもたくさんあります。(DB設計者の好みだし)
また、アプリケーションでも多々「簡単なSQLなのにConditionBeanが利用できないや」
ということが多くあるかと思います。
そこで、AdditionalForeignKeyに「業務的な固定の条件」を付与できるようにしました。
自動判別はどうしても無理なのですが、明示的に指定することによって可能にします。
map:{
; FK_MEMBER_VALID_MEMBER_ADDRESS = map:{
; localTableName = MEMBER ; foreignTableName = MEMBER_ADDRESS
; fixedCondition = $$foreignAlias$$.VALID_FLG = 1
}
}
※「$$foreignAlias$$」は動的に結合時の「結合先Alias名(Foreign)」として解決されます
すると、
MemberCB member = new MemberCB();
member.setupSelect_MemberAddress();
で実装できるようになり、SQLは以下のようになります。
from MEMBER mb
left outer join MEMBER_ADDRESS adr
on mb.MEMBER_ID = adr.MEMBER_ID and adr.VALID_FLG = 1
これがあれば、DB設計者も安心して設計することができますし、
プログラマも先ほどのDB構造で悩むこともないかと思います。
ちなみに
map:{
; FK_MEMBER_VALID_MEMBER_ADDRESS = map:{
; localTableName = MEMBER ; foreignTableName = MEMBER_ADDRESS
; fixedCondition = $$alias$$.VALID_FLG = 1 ; fixedSuffix = AsValid
}
}
とすると、
member.setupSelect_MemberAddress();
↓
member.setupSelect_MemberAddressAsValid();
というように関連プロパティ名に意味をつけることが出来ます。
もし、同じテーブルに対してfixedConditionだけが違う関連を2つ付ける場合は、
これを利用してプロパティ名をユニークにします。
もちろん、明示的に意味のあるものを付けるということで、
そうで無い場合でもfixedSuffixを付けることを推奨します。
また、これは、上記のようなパターンだけでなく、
「何かを指定したらone-to-one」という関連にも利用できます。
o 会員(MEMBER)テーブル
- MEMBER_ID (PK)
- MEMBER_NAME
o 会員メルマガ情報(MEMBER_MAIL_MAGAZINE)テーブル
- MEMBER_MAIL_MAGAZINE_ID (PK)
- MEMBER_ID (FK and UQ)
- MAIL_MAGAZINE_TYPE (UQ) {aaa / bbb / ccc の3つ}
この場合、MAIL_MAGAZINE_TYPE = 'aaa'を指定すればone-to-oneになります。
そして、このような結合も実務では頻繁に発生します。
map:{
; FK_MEMBER_MEMBER_MAIL_MAGAZINE_AAA = map:{
; localTableName = MEMBER ; foreignTableName = MEMBER_ADDRESS
; fixedCondition = $$alias$$.MAIL_MAGAZINE_TYPE = 'aaa'
; fixedSuffix = TypeAaa
}
}
↓
MemberCB member = new MemberCB();
// 'aaa'を前提とした結合
member.setupSelect_MemberAddressTypeAaa();
↓
from MEMBER mb
left outer join MEMBER_MAIL_MAGAZINE mgz
on mb.MEMBER_ID = mgz.MEMBER_ID and mgz.MAIL_MAGAZINE_TYPE = 'aaa'
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
追記:(2007/10/23)
$$alias$$ を $$foreignAlias$$ に修正しました。
ローカルテーブルのAlias名も置換したい場合に$$localAlias$$とするため、
命名の対照性を合わせました。m_m
※但し、$$alias$$はそのまま動作します。
$$alias$$.equals($$foreignAlias$$))
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
追記:(2008/12/01)
http://d.hatena.ne.jp/jflute/20081201/1228126523
にてバインド変数で扱えるようになりました。
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =