カジュアルな技術ノート

小難しい技術のお話をできるだけわかりやすく...

【DBFlute】SQL はどう作られてるの?ConditionBean と Behavior の責務

http://dbflute.seasar.org/image/parts/top/logo.png

ビズリーチで開いていただいてる隔週の jflute さん勉強会。
先日 DBFlute の ConditionBean のソースコードリーディングをやっていただきました。
ConditionBean のソースコードを読み進める中で、内部構造のこととかいくつか興味深い話があったのでまとめますー。

この記事で扱うこと

  • 表示 SQL ってどこで生成されているのか
  • SQL 生成における ConditionBean と Behavior の責務

DBFlute の表示 SQL

DBFlute 使ってガシガシ DB アクセスしている人ならとても馴染みの深い機能である SQL の表示機能。
例えばこんなコードで欲しいデータを表示させると。。

memberBhv.selectEntity(cb -> {
    cb.query().setMemberId_Equal(1);
    cb.query().setMemberName_LikeSearch("S", LikeSearchOption::likePrefix);
});

ログにはこんな感じで、対応した SQL が表示されます。

select dfloc.MEMBER_ID as MEMBER_ID, dfloc.MEMBER_NAME as MEMBER_NAME, ....
  from MEMBER dfloc
 where dfloc.MEMBER_ID = 1
   and dfloc.MEMBER_NAME like 'S%' escape '|'

指定した ConditionBean が期待した通りになっているのか SQL から確認できるという便利な機能です。
この SQL って一体どこで作られているのでしょうか?
実は完全に ConditionBean の責務となっていて ConditionBean 単体で toDisplay メソッドによって SQL を表示させられます!

MemberCB memberCB = new MemberCB();
memberCB.query().setMemberId_Equal(1);
memberCB.query().setMemberName_LikeSearch("S", LikeSearchOption::likePrefix);
System.out.println(memberCB.toDisplaySql());

本家のドキュメントを見るとこちらに詳細があります。

dbflute.seasar.org

ConditionBean -> 2WaySQL -> 表示 SQL

さてこの toDisplaySql メソッドの中を見てみるとわかるのですが、ConditionBean から表示 SQL を直接生成しているのではなく一度 2WaySQL に変換しています。
実際に対応する 2WaySQL は次で表示できます。

MemberCB memberCB = new MemberCB();
memberCB.query().setMemberId_Equal(1);
memberCB.query().setMemberName_LikeSearch("S", LikeSearchOption::likePrefix);
System.out.println(memberCB.getSqlClause().getClause());

表示される 2WaySQL です。

select/*$pmb.selectHint*/ dfloc.MEMBER_ID as MEMBER_ID, dfloc.MEMBER_NAME as MEMBER_NAME, ....
  from MEMBER dfloc
 where dfloc.MEMBER_ID = /*pmb.conditionQuery.memberId.fixed.query.equal*/null
   and dfloc.MEMBER_NAME like /*pmb.conditionQuery.memberName.varying.likeSearch.likeSearch0*/null escape '|'

この 2WaySQL をさらに表示 SQL 変換しているという処理の流れです。

ConditionBean と Behavior の責務

なぜ一度 2WaySQL に変換するのか。。
jflute さんによると、そもそも ConditionBean は指定した絞り込み条件を 2WaySQL に変換することが大きな責務となってるため、それならば 2WaySQL から変換した方が簡単であるためとのことです。
そして生成された 2WaySQL を Behavior クラスに渡して、実行用の SQL に変換し実際に DB に実行してもらうと言う設計になっています。

つまり SQL 生成については

f:id:shin-kinoshita:20190310204103p:plain
SQL 生成での ConditionBean と Behavior の責務
という役割分担と言うことです。

なぜ ConditionBean は 2WaySQL を生成して Behavior クラスに渡すのでしょうか?
これは DBFlute は DB に対して SQL を実行するときはバインド変数を使用していますが、その時の変数のパースが 2WaySQL だとやりやすかったため、採用したとのことでした。
ちなみに外だし SQL と比較してみるとどちらも 2WaySQL を生成することを行なっていますが、

  • ConditionBean: ConditionBean クラスが 2WaySQL を生成
  • 外だし SQL: 開発者が手動で 2WaySQL を生成

という違いになっているとのことです。

まとめ

  • 表示 SQL は ConditionBean で生成
  • ConditionBean の責務は 2WaySQL を生成すること
  • Behavior の責務は 2WaySQL を実行 SQL に変換して実行すること

知っているから即役立つと言ったものでもないですが DBFlute の内部構造のお話でした。