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 を基本にすることをお奨めしています。