ConditionBeanのスコープ
概要
DBFluteは、DBアクセスの大きな2つのやり方を提供しています。それはConditionBeanと外だしSQL(OutsideSql)です。これら2つの存在意義に関してはここでは割愛させて頂き、いざ実装するときにこの2つのどちらを利用すれば良いのかに焦点を当てます。
当然、「ConditionBeanでできないことは外だしSQLで」という正論に間違いはありませんが、プログラマがConditionBeanの機能を頭の中で全て抑えていることは少ないと思われます。そこで、素早く判断するための「判断の流れ」を紹介したいと思います。
判断の流れ
A. 明らかな外だしSQLか否かを判断
ConditionBeanのスコープを確認する前に、まずは「明らかに外だしSQLでやるべき」と言えるものを確認していきます。こうすることで、ConditionBeanを意識する前の早い段階で判断が可能です。
以下に当てはまるものは確実に外だしSQLです。
- Group By句を使う
- max(),min(),sum(),avg(),count()以外の関数を使う
- Merge構文やMinus構文などを使う
- 更新ロック以外のロック指定(DB2の「with RR」やMySQLの「lock table」など)
-
カーソルで1レコードずつメモリ展開して処理したい
- この場合はOutsideSqlのカーソル検索を利用
- カーソル検索についてはこちら
B. ConditionBeanのスコープを判断
そして、ConditionBeanのスコープからConditionBeanで実装できるか確認していきます。
「明らかな外だしSQL」を通り過ぎたこの時点ではConditionBeanで実装可能である可能性が高いです。実際にConditionBeanを実装しながらの確認でも構いません。後に「やはり外だしSQLで実装」となっても、そのConditionBeanの実装は無駄にはなりません。それは「C」において後述しますが、近いところまで実装したConditionBeanから外だしSQLの土台となるSQLが生成できるからです。
以下がConditionBeanのスコープです。
Select句
-
取得するカラムは基本的に基点テーブルの全てのカラムとSetupSelect指定されたテーブルの全てのカラム
- 取得するカラムを基点テーブルの主キーだけに絞ることも可能
- 取得するカラムを明示的に指定することも可能
- 「select count(*)」も可能
- 基点テーブルのカラムにおける「max(),min(),sum(),avg()」も可能
-
結合先カラムの値を取得
- 設定できる結合先は無限階層
- どんなテーブルが結合先となりうるかはFrom句の欄を参照
-
子テーブルのとあるカラムの{max(),min(),sum(),avg(),count()}を取得
- Select句内の相関サブクエリ
- 子テーブルに絞り込み条件を付与することも可能
From句
-
結合先はManyToOne/OneToOneの関係になるテーブルのみ
- 親テーブル(ManyToOne)
- → ex) 会員ステータス(MEMBER_STATUS)
- OneToOneの子テーブル
- → ex) 会員退会情報(MEMBER_WITHDRAWAL)
- AdditionalForeignKeyのFixedConditionでOneToOneに調整できる子テーブル
- → ex) 最終ログイン(MEMBER_LOGIN)
※「ex」は会員(MEMBER)が基点であることを前提としています。
- 親テーブル(ManyToOne)
- 結合の指定はSelect句とWhere句の指定から全て自動で判別されるので明示的に指定する必要はない
-
結合方法は全てleft outer join限定
- inner joinはできないが「where 基点FK is not null」という条件を組み立てることで代替は可能
-
結合先だけを絞り込む(結合前に絞り込む)のサブクエリ(インラインビュー)
- サブクエリ内で利用できる絞り込み条件の仕様はConditionBeanで使えるものと同様
Where句
-
複数条件の連結は全てAnd条件
- Or句はUnionを利用することにより実現可能 {Unionの詳細はUnion句の欄を参照}
-
Where句条件で以下が利用可能
- 基本演算子 {=, !=, >, >=, <, <=}
- InScope {in ('a', 'b')}
- NotInScope {not in ('a', 'b')}
- Like条件(前方/後方/中間)とエスケープ {like 'S%' escape '|'}
- IsNull / IsNotNull {is null / is not null}
-
結合先カラムでの絞り込み条件(order byも含む)
- 設定できる結合先は無限階層
- どんなテーブルが結合先となりうるかはFrom句の欄を参照
-
定型的なサブクエリを使った条件
- 親テーブル(結合可能テーブル)のInScopeSubQuery {基点FK in (select 親PK from 親 where ...)}
- 子テーブルのInScopeSubQuery {基点PK in (select 子FK from 子 where ...)}
- 子テーブルのExistsSubQuery {exists (select 子PK from 子 where 子FK = 基点PK and ...)}
- サブクエリ内で利用できる絞り込み条件の仕様はConditionBeanで使えるものと同様
OrderBy句
- 昇順ソート/降順ソート
- 複数カラムのソート
-
結合先カラムでのソート条件
- 設定できる結合先は無限階層
- 昇順/降順/複数カラム指定可能
Union句
-
基点テーブル同士でのUnion {但しC#版は不可(2008年1月現在)}
- UnionAllも可能
- Unionの数は無限
その他
- ページング検索
- 更新ロック
-
子テーブルをOneToManyの関係のまま取得 {BehaviorのLoadReferrer}
- SubSelectフェッチになる(別SQL1発で関連している子テーブルのレコードを全て取得)
- 子テーブルの絞り込み条件やソート条件はConditionBeanで使えるものと同様
- {1:n:n}というように無限階層そして枝分かれ階層のLoadが可能 ※但しC#版は不可(2008年1月現在)
C. やはり外だしSQLであると判断
ここまで来たら、やはり「外だしSQLで」ということになります。
ここで、ちょっとした「外だしSQL」の実装支援を紹介します。 もし、この時点でConditionBeanで近いところまで実装済みであれば、それは消してはいけません。以下の手順が可能だからです。
- ConditionBeanでできるところまでテスト実装
- ConditionBeanのtoDisplaySql()の戻り値をログに出力
- ログ出力されたSQL文を外だしSQLの土台として活用
このようにすることで、外だしSQLの実装にてSQLの構文やテーブル名、カラム名を1から書く必要はなくなり、スペルミスなどのケアレスミスもなくなります。また、判断の流れの中で実装したConditionBeanは全く無駄になりません。
但し、ConditionBeanのtoDisplaySql()は、「Java版でDBFlute-0.6.0から」、「C#版でDBFlute-0.6.7から」の機能です。
