はじめに
前回は、ConditionBeanの条件組立ての基本機能をみていきました。
今回は、結合やソートの基本をみていきましょう。
ConditionBean
ConditionBeanにおける結合とは?
まず、ConditionBeanにおける結合とは何を示すかを意識合わせしておきましょう。結合のそのものの意味では単に「テーブルを結合する」ことだけを示します。結合すること自体は「手段」であり「目的」ではありません。
では、目的はどんなものがあるかというと、主に次の2つに整理できます。
- 結合先テーブルのデータを取得すること
- 結合先テーブルの条件で結果を絞り込むこと
ConditionBeanにおける結合は、このような目的を意識した仕様となっています。具体的には、プログラマはConditionBeanに対して上記の目的をそのまま伝えます。そして、結合をするかしないかは、指定された目的を基に自動で判別します。
例えば、「結合先テーブルの条件で結果を絞り込むこと」という目的を達したい場合は、以下(リスト1、2)のようになります。
また、「結合先テーブルの条件で結果を絞り込むこと」という目的を達したい場合は、以下(リスト3、4)のようになります。
無論、これら目的が同居する場合は、以下(リスト5、6)のようになります。
結合に関連する目的が2つ指定されたからといって、SQL上でJoin節が2つ出力されてしまうことはありません。
ConditionBeanでは、結合先テーブルの条件を追加したからといって、勝手に結合先テーブルのデータを取得することはしません。
実際の業務ロジックでは、「結合先テーブルの条件で絞り込んでデータとして取得するのは基点テーブルのみ」ということも多々あります。不要な結合先のテーブルのデータは取得しないようにするべき、というのがConditionBeanの思想です。
「呼び出し側プログラムが必ず何のテーブルを取得したいのか? (絞込み条件に関わらず)を意識するべき」という考えのもと設計されています。
結合先テーブルのデータを取得すること
まずは、先述した目的の一つ「結合先テーブルのデータを取得すること」からみていきましょう。
実は、既に説明されてしまっている部分もありますが、しっかり単体テストの形式で構造的に追っていきます。
結合して親テーブルの取得
まずは、結合して親テーブルのデータを取得するやり方です(リスト7)。
cb.setupSelect_MemberStatus()を呼び出していることがポイントです。
会員Entityから会員ステータスEntityが取得できます。
もし、setupSelect_MemberStatus()が呼び出されなかった場合は、getMemberStatus()がnullを戻します。
また、setupSelect_MemberStatus()を呼び出されていても、そのFK列がNullの会員はgetMemberStatus()がnullを戻します。
この例題の場合は、FK列の会員ステータスコードにNotNull制約があり、会員に対して会員ステータスが必ず存在するため、「getMemberStatus()が絶対にNullを戻さないこと」というように確認をしています。
結合してone-to-oneテーブルの取得
テーブル設計において、one-to-one関連のテーブルを作成することがよくあります。
表現の仕方は様々ですが、ここでのone-to-oneはスキーマ例の「会員セキュリティ」・「会員退会」などのようなone-to-manyのmany側のFK列にユニークな制約(PrimaryKeyなど)が掛かっているような構造のものを示します。
そして、そのone-to-oneテーブルも結合して取得することが可能です(リスト8)。cb.setupSelect_MemberSecurityAsOne()を呼び出していることがポイントです。
会員Entityから会員セキュリティ情報Entityが取得できます。
もし、setupSelect_MemberSecurityAsOne()が呼び出されなかった場合は、getMemberStatus()がnullを戻します。
また、setupSelect_MemberStatus()を呼び出していても、該当の会員セキュリティ情報が存在しない会員の場合は、getMemberStatus()がnullを戻します。
この例題の場合は、テストデータとして会員に対して会員セキュリティが必ず存在するようにしているため、「getMemberSecurityAsOne()が絶対にNullを戻さないこと」というように確認をしています。
また、以後説明の中で「親テーブル」と言った場合に、この「one-to-oneテーブル」も含みます。
実際には親テーブルではありませんが、DBFlute上では「one-to-oneであれば親テーブルのように扱うことができる」ため、そのように表現させて頂きます。
結合して親の親テーブルの取得
そして、親の親テーブルの取得です。
まずは、結合して親の親テーブルのデータを取得するやり方です(リスト9)。cb.setupSelect_MemberWithdrawalAsOne().withWithdrawalReason()を呼び出していることがポイントです。
会員Entityから会員退会情報Entityを取得して、その会員退会情報Entityから退会理由Entityが取得できます。
この例題の場合は、会員退会情報は全ての会員が保持しているわけではないので、会員退会情報Entityのnullチェックを行っています。
退会理由Entityは会員退会情報があれば必須なので(NotNull制約あり)、「getWithdrawalReason()が絶対にNullを戻さないこと」というように確認をしています。
親テーブルの取得の制限
親テーブルを取得するsetupSelect_Xxx()メソッドならびにwithXxx()メソッドに関してですが、2階層目の親までという制限があります。つまり、親の親の親テーブルを結合して取得することはできません。
これは、フレームワーク上の都合として現状はこのような仕様になっております。
業務利用上、3階層以上の親を結合して取得したい場合はそこまで多くはないため、実際の支障は少ないですが、いずれ改善はしたいと考えている部分ではあります。
よく勘違いされやすいのですが、「条件絞りをするための結合」の制限ではありません。
そちらの結合の方は、親方向に無限階層で条件を指定可能です。なので、親テーブルの結合の制限ではなく「親テーブルの取得の制限」という表現をしています。
結合先テーブルの条件で結果を絞り込むこと
次は、結合のもう一つの目的「結合先テーブルの条件で結果を絞り込むこと」をみていきましょう。
結合して親テーブルの条件で結果を絞り込むやり方です(リスト10)。cb.query().queryMemberStatus().set...というように親テーブルでの条件を指定しているところがポイントです。
先述の通り、one-to-oneテーブルも親テーブルとして扱うことが可能なため、「親テーブルの条件で絞込み」の例題のように指定して条件を付けることが可能です。
また、queryXxx()メソッドは、親方向に無限階層指定することが可能なため、SetupSelectの「親テーブルの取得の制限」というような階層制限は存在しません。
ConditionBeanでソート指定
次はソートのやり方をみていきましょう。
ソートに関してはあまり深い概念は存在しませんので、プログラムをみてもらうのが一番早いかと思われます(リスト11)。cb.query().addOrderBy_MemberAccount_Desc()というようにソート条件を指定しているところがポイントです。
AscなのかDescなのかは、メソッド指定で決定します。
複数のソート条件を指定したい場合は、addOrderBy_Xxx...()を順次呼び出していけば、呼び出した順番でソート条件が追加されます。
ConditionBeanでの基本検索
それでは、おおよその基本をみてきたので、ここでそれらを組み合わせた検索をみておきましょう(リスト12、13)。
今までに登場してきた条件絞込み・結合・ソートを組み合わせた検索になっております。これら基本機能だけで、アプリケーションにて発行するSQLの多くを表現することが可能でしょう。
次回
これにてConditionBeanの基本は一通り抑えました。実を言うと応用がたくさん存在するのですが、それらはまた後で説明させて頂きます。
次回は、いざというときに大活躍する「外だしSQL(OutsideSql)」をみていきましょう!