DBアクセスを定番化しよう DBFlute入門

第4回 ConditionBeanにおける結合やソート

この記事を読むのに必要な時間:およそ 7.5 分

結合先テーブルのデータを取得すること

まずは,先述した目的の一つ「結合先テーブルのデータを取得すること」からみていきましょう。

実は,既に説明されてしまっている部分もありますが,しっかり単体テストの形式で構造的に追っていきます。

結合して親テーブルの取得

まずは,結合して親テーブルのデータを取得するやり方ですリスト7⁠。

cb.setupSelect_MemberStatus()を呼び出していることがポイントです。

リスト7:結合して親テーブルの取得

/**
 * 結合して会員ステータス(親テーブル)を取得する会員一覧を検索
 * 
 * @throws Exception
 */
public void test_ConditionBean_SetupSelect_ForeignTable_Tx() throws Exception {
// ## Arrange ##
    final MemberCB cb = new MemberCB();
    cb.setupSelect_MemberStatus();

    // ## Act ##
    final List memberList = memberBhv.selectList(cb);

    // ## Assert ##
    for (Member member : memberList) {
        final MemberStatus memberStatus = member.getMemberStatus();
        assertNotNull(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()を呼び出していることがポイントです。

リスト8:結合して親テーブルの取得

/**
 * 結合して会員セキュリティ情報(one-to-oneテーブル)を取得する会員一覧を検索
 * 
 * @throws Exception
 */
public void test_ConditionBean_SetupSelect_ReferrerAsOneTable_Tx() throws Exception {
    // ## Arrange ##
    final MemberCB cb = new MemberCB();
    cb.setupSelect_MemberSecurityAsOne();

    // ## Act ##
    final List memberList = memberBhv.selectList(cb);

    // ## Assert ##
    for (Member member : memberList) {
        final MemberSecurity memberSecurityAsOne = member.getMemberSecurityAsOne();
        log.debug("memberSecurityAsOne=" + memberSecurityAsOne);
        assertNotNull(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()を呼び出していることがポイントです。

リスト9:結合して親の親テーブルの取得

/**
 * 結合して会員退会情報と退会理由(親テーブル&親の親テーブル)を取得する会員一覧を検索
 * 
 * @throws Exception
 */
public void test_ConditionBean_SetupSelect_ForeignTable_with_Foreign_Tx() throws Exception {
    // ## Arrange ##
    final MemberCB cb = new MemberCB();
    cb.setupSelect_MemberWithdrawalAsOne().withWithdrawalReason();

    // ## Act ##
    final List memberList = memberBhv.selectList(cb);

    // ## Assert ##
    boolean existsOneOrMoreAtLeast = false;
    for (Member member : memberList) {
        final MemberWithdrawal memberWithdrawalAsOne = member.getMemberWithdrawalAsOne();
        log.debug("memberWithdrawalAsOne=" + memberWithdrawalAsOne);
        if (memberWithdrawalAsOne != null) {
            existsOneOrMoreAtLeast = true;
            final WithdrawalReason withdrawalReason = memberWithdrawalAsOne.getWithdrawalReason();
            log.debug("    withdrawalReason=" + withdrawalReason);
            assertNotNull(withdrawalReason);
        }
    }
    // 少なくとも一件以上は退会者の情報を持っていることのチェック(テスト成立確認)
    assertTrue(existsOneOrMoreAtLeast);
}

会員Entityから会員退会情報Entityを取得して,その会員退会情報Entityから退会理由Entityが取得できます。

この例題の場合は,会員退会情報は全ての会員が保持しているわけではないので,会員退会情報Entityのnullチェックを行っています。

退会理由Entityは会員退会情報があれば必須なので(NotNull制約あり⁠⁠,⁠getWithdrawalReason()が絶対にNullを戻さないこと」というように確認をしています。

親テーブルの取得の制限

親テーブルを取得するsetupSelect_Xxx()メソッドならびにwithXxx()メソッドに関してですが,2階層目の親までという制限があります。つまり,親の親の親テーブルを結合して取得することはできません。

これは,フレームワーク上の都合として現状はこのような仕様になっております。

業務利用上,3階層以上の親を結合して取得したい場合はそこまで多くはないため,実際の支障は少ないですが,いずれ改善はしたいと考えている部分ではあります。

よく勘違いされやすいのですが,⁠条件絞りをするための結合」の制限ではありません。

そちらの結合の方は,親方向に無限階層で条件を指定可能です。なので,親テーブルの結合の制限ではなく「親テーブルの取得の制限」という表現をしています。

著者プロフィール

久保雅彦(くぼまさひこ)

DBFluteメインコミッタ。主にオープン系の開発に従事。DB設計・DB周りの実装などを担当することが多い。