ExistsReferrer
概要
基本概念
子テーブルの条件で絞り込みをする条件(exists (select ... from ...))を設定します。ExistsReferrer は、絞り込み条件を表す ConditionKey です。
例えば、"モバイルからログインをしたことのある会員(一覧)" というように、 子テーブル(会員ログイン情報)のカラムの条件を利用して、基点テーブル(会員)を絞り込むのに有効です。 また、子テーブル(one-to-many) だけでなく、"とある名前の商品を購入したことのある会員(一覧)" というように、子テーブルの親テーブル(many-to-many)のカラムを利用した絞り込みにも利用できます。 このような条件が exists 句を利用した相関サブクエリで実現されます。
会話上では、いぐじすつりふぁらぁ と表現します。
子テーブル対応の役割を明確に
これは、あくまで子テーブルを使った絞り込みであって、子テーブルのデータ取得(LoadReferrer)ではありません。 DBFluteでは、この違いを混同させずに機能として明確に分けています。
実装方法
実装の流れ
query() の後、exists[referrer-table]List() を呼び出し、SubQuery のコールバック実装を引数に指定します。
e.g. ExistsReferrerの実装手順 (Eclipseでコード補完) {PURCHASE} @Java
MemberCB cb = new MemberCB();
cb.q // .q と打って enter
--
cb.query()
--
// 1. .ex まで打つと関連テーブル選択
// 2. PL (PurchaseList) で enter
cb.query().exPL
--
// メソッドが補完されて、引数の "subQuery" が選択状態に
cb.query().existsPurchaseList(subQuery)
--
// "new " (new + 空白一つ) と打って ctrl + space そして enter
cb.query().existsPurchaseList(new )
--
// 実装メソッドの空実装が自動生成される (Eclipse-3.5 以上)
cb.query().existsPurchaseList(new SubQuery<PurchaseCB>() {
public void query(PurchaseCB subCB) {
// TODO Auto-generated method stub
}
})
--
// ctrl (or command) + D で不要な空行やTODOコメントを消して
// サブクエリ(子テーブル)の絞り込み条件を指定
cb.query().existsPurchaseList(new SubQuery<PurchaseCB>() {
public void query(PurchaseCB subCB) {
// 2000円以上の購入をしたことのある会員
subCB.query().setPurchasePrice_GreaterEqual(2000);
}
}); // セミコロンを忘れずに
SQL上では、FKを構成する関連カラムを使った相関条件が自動的に付与されます。
e.g. 2000円以上の購入をしたことのある会員 @DisplaySql
...
from MEMBER dfloc
where exists (select sub1loc.MEMBER_ID
from PURCHASE sub1loc
where sub1loc.MEMBER_ID = dfloc.MEMBER_ID
and sub1loc.PURCHASE_PRICE >= 2000
)
子テーブルの親テーブル (many-to-many)
子テーブルの親テーブル(many-to-many)のカラムも条件として利用できます。ExistsReferrer の中で Query(Relation) を使って実現します。
e.g. 商品名が "S" で始まる商品を購入したことのある会員 @Java
MemberCB cb = new MemberCB();
cb.query().existsPurchaseList(new SubQuery<PurchaseCB>() {
public void query(PurchaseCB subCB) {
subCB.query().queryProduct().setProductName_PrefixSearch("S");
}
});
子テーブルの子テーブル (one-to-many-to-many)
子テーブルの子テーブル(one-to-many-to-many)のカラムも条件として利用できます。ExistsReferrer の中でさらに ExistsReferrer を使って実現します。
e.g. 2000円以上の購入をしたことのある会員の会員ステータス @Java
MemberStatusCB cb = new MemberStatusCB();
cb.query().existsMemberList(new SubQuery<MemberCB>() {
public void query(MemberCB subCB) {
subCB.query().existsPurchaseList(new SubQuery<PurchaseCB>() {
public void query(PurchaseCB subCB) {
subCB.query().setPurchasePrice_GreaterEqual(2000);
}
});
}
});
親テーブルの子テーブル (many-to-one-to-many)
親テーブルの子テーブル(many-to-one-to-many)のカラムも条件として利用できます。Query(Relation) の後に ExistsReferrer を使って実現します。
e.g. 2000円以上の購入をしたことのある会員の会員ログイン @Java
MemberLoginCB cb = new MemberLoginCB();
cb.query().queryMember().existsPurchaseList(new SubQuery<PurchaseCB>() {
public void query(PurchaseCB subCB) {
subCB.query().setPurchasePrice_GreaterEqual(2000);
}
});
サブクエリが空条件でも意味あり
サブクエリに何も条件がなくても、ExistsReferrer は有効です。会員と購入の関係で言えば、"(一度でも何かしら)購入したことのある会員(一覧)" という業務的な条件を意味します。
e.g. 空条件のExistsReferrer {MEMBER, PURCHASE} @Java
MemberCB cb = new MemberCB();
cb.query().existsPurchaseList(new SubQuery<PurchaseCB>() {
public void query(PurchaseCB subCB) {
// 空条件:(一度でも何かしら)購入したことのある会員(一覧)
}
});
e.g. 空条件のExistsReferrer {MEMBER, PURCHASE} @DisplaySql
...
from MEMBER dfloc
where exists (select sub1loc.MEMBER_ID
from PURCHASE sub1loc
where sub1loc.MEMBER_ID = dfloc.MEMBER_ID
)
もし、サブクエリ内の条件値が存在しないときに ExistsReferrer 自体を無効にしたいというのであれば、if 文で分岐させます。
e.g. 空条件のExistsReferrer {MEMBER, PURCHASE} @Java
MemberCB cb = new MemberCB();
final Integer price = ...;
if (price != null) {
cb.query().existsPurchaseList(new SubQuery<PurchaseCB>() {
public void query(PurchaseCB subCB) {
subCB.query().setPurchasePrice_GreaterEqual(price);
}
});
}
メソッド仕様
基本仕様
- 引数の指定
- 引数の SubQuery は必須です。
- 同テーブルに対する複数条件の指定
- 同じ関連テーブルに対しての ExistsReferrer 複数回呼び出しは、呼び出した分だけ条件になります。
- サブクエリのConditionBean
- サブクエリの ConditionBean は、絞り込み条件だけの指定に利用するものです。 SetupSelect や OrderBy などサブクエリとして必要のない機能は呼び出してはいけません。
- OnClause や InlineView では不可
- OnClause や InlineView の中の条件では利用できません。
サポートされる関連テーブル
one-to-many に加えて、one-to-one (業務的one-to-oneを除く)の関連に対してサポートされます。
但し、one-to-one は、DBFluteでは ForeignTable として扱われ、Query(Relation) で簡単に条件を設定できるので、ExistsReferrer を使う必要性はあまりないかもしれません。 (本来、実装上で Referrer と言った場合、基本的に one-to-one は含まれません。昔々に作って消すタイミングを失ってしまったとも言えます)
否定条件:NotExistsReferrer
否定条件の対象値の列挙として、NotExistsReferrer もあります。条件が否定になっただけで仕様は ExistsReferrer と全く同じです。cb.query().exists... ではなく cb.query().notExists... と書きます。
代替方法:InScopeRelation
全く同じ結果を返す別の方法として、InScopeRelation があります。DBFluteとしては、基本的に "子テーブルの条件で絞り込み" をする場合は、ExistsReferrer を基本にすることをお奨めしています。