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

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

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

結合先テーブルの条件で結果を絞り込むこと

次は,結合のもう一つの目的「結合先テーブルの条件で結果を絞り込むこと」をみていきましょう。

結合して親テーブルの条件で結果を絞り込むやり方ですリスト10⁠。cb.query().queryMemberStatus().set...というように親テーブルでの条件を指定しているところがポイントです。

リスト10:結合して親テーブルの条件で結果を絞込み

/**
 * 結合した会員ステータスの名称が“正”で始まる会員一覧を検索
 * 
 * @throws Exception
 */
public void test_ConditionBean_QueryForeign_ForeignTable_Tx() throws Exception {
    // ## Arrange ##
    final MemberCB cb = new MemberCB();
    cb.query().queryMemberStatus().setMemberStatusName_PrefixSearch("正");

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

    // ## Assert ##
    final MemberStatusCB memberStatusCB = new MemberStatusCB();
    memberStatusCB.query().setMemberStatusName_PrefixSearch("正");
    final MemberStatus memberFormalizedStatus = memberStatusBhv.selectEntityWithDeletedCheck(memberStatusCB);
    final String formalizedCode = memberFormalizedStatus.getMemberStatusCode();
    for (Member member : memberList) {
        log.debug("member=" + member);
        final String memberStatusCode = member.getMemberStatusCode();
        assertTrue("正式会員を示すコードであること", memberStatusCode.equals(formalizedCode));
        assertNull("会員ステータス自身は取得はしていないこと", member.getMemberStatus());
    }
}

先述の通り,one-to-oneテーブルも親テーブルとして扱うことが可能なため,⁠親テーブルの条件で絞込み」の例題のように指定して条件を付けることが可能です。

また,queryXxx()メソッドは,親方向に無限階層指定することが可能なため,SetupSelectの「親テーブルの取得の制限」というような階層制限は存在しません。

ConditionBeanでソート指定

次はソートのやり方をみていきましょう。

ソートに関してはあまり深い概念は存在しませんので,プログラムをみてもらうのが一番早いかと思われますリスト11⁠。cb.query().addOrderBy_MemberAccount_Desc()というようにソート条件を指定しているところがポイントです。

リスト11:基点テーブルの列で並べて検索

/**
 * 会員アカウントの降順で並べた会員一覧を検索
 * 
 * @throws Exception
 */
public void test_ConditionBean_Query_AddOrderBy_Tx() throws Exception {
    // ## Arrange ##
    final MemberCB cb = new MemberCB();
    cb.query().addOrderBy_MemberAccount_Desc();

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

    // ## Assert ##
    String preMemberAccount = null;
    for (Member member : memberList) {
        final String memberName = member.getMemberName();
        final String memberAccount = member.getMemberAccount();
        log.debug("memberName=" + memberName + " memberAccount=" + memberAccount);
        if (preMemberAccount == null) {
            preMemberAccount = memberAccount;
            continue;
        }
        if (preMemberAccount.compareTo(memberAccount) > 0) {
            // OK
        } else if (preMemberAccount.compareTo(memberAccount) == 0) {
            // OK
        } else {
            fail();
        }
    }
}

AscなのかDescなのかは,メソッド指定で決定します。

複数のソート条件を指定したい場合は,addOrderBy_Xxx...()を順次呼び出していけば,呼び出した順番でソート条件が追加されます。

ConditionBeanでの基本検索

それでは,おおよその基本をみてきたので,ここでそれらを組み合わせた検索をみておきましょうリスト12,13⁠。

今までに登場してきた条件絞込み・結合・ソートを組み合わせた検索になっております。これら基本機能だけで,アプリケーションにて発行するSQLの多くを表現することが可能でしょう。

リスト12:基本検索

/**
 * 以下の条件の会員一覧を検索。
 * ・会員ステータスと退会理由を取得
 * ・会員アカウントが“M”で始まる
 * ・会員ステータス.表示順の昇順,会員.誕生日の降順,会員.会員IDの昇順でソート
 * 
 * @throws Exception
 */
public void test_ConditionBean_BasicSelect_Tx() throws Exception {
    // ## Arrange ##
    final MemberCB cb = new MemberCB();
    cb.setupSelect_MemberStatus();
    cb.setupSelect_MemberWithdrawalAsOne().withWithdrawalReason();
    cb.query().setMemberAccount_PrefixSearch("M");
    cb.query().queryMemberStatus().addOrderBy_DisplayOrder_Asc();
    cb.query().addOrderBy_MemberBirthday_Desc();
    cb.query().addOrderBy_MemberId_Asc();

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

    // ## Assert ##
    for (Member member : memberList) {
        final String memberAccount = member.getMemberAccount();
        final Date memberBirthday = member.getMemberBirthday();
        final String memberStatusName = member.getMemberStatus().getMemberStatusName();
        final MemberWithdrawal memberWithdrawalAsOne = member.getMemberWithdrawalAsOne();
        String withdrawalReasonText = null;
        if (memberWithdrawalAsOne != null) {
            final WithdrawalReason withdrawalReason = memberWithdrawalAsOne.getWithdrawalReason();
            withdrawalReasonText = withdrawalReason.getWithdrawalReasonText();
        }
        log.debug(memberAccount + " - " + memberBirthday + " - " + memberStatusName + " - " + withdrawalReasonText);
    }
}

リスト13:基本検索のSQL

select MEMBER.MEMBER_ID
     , MEMBER.MEMBER_NAME
     , MEMBER.MEMBER_ACCOUNT
     , MEMBER.MEMBER_STATUS_CODE
     , MEMBER.MEMBER_BIRTHDAY
     , MEMBER.VERSION_NO
     , MEMBER.MEMBER_FORMALIZED_DATETIME
     , MEMBER.REGISTER_DATETIME
     , MEMBER.REGISTER_USER
     , MEMBER.REGISTER_PROCESS
     , MEMBER.UPDATE_DATETIME
     , MEMBER.UPDATE_USER
     , MEMBER.UPDATE_PROCESS
     , dbfluteRelno_0.MEMBER_STATUS_CODE AS MEMBER_STATUS_CODE_0
     , dbfluteRelno_0.MEMBER_STATUS_NAME AS MEMBER_STATUS_NAME_0
     , dbfluteRelno_0.DISPLAY_ORDER AS DISPLAY_ORDER_0
     , dbfluteRelno_2.MEMBER_ID AS MEMBER_ID_2
     , dbfluteRelno_2.VERSION_NO AS VERSION_NO_2
     , dbfluteRelno_2.REGISTER_DATETIME AS REGISTER_DATETIME_2
     , dbfluteRelno_2.REGISTER_USER AS REGISTER_USER_2
     , dbfluteRelno_2.REGISTER_PROCESS AS REGISTER_PROCESS_2
     , dbfluteRelno_2.UPDATE_DATETIME AS UPDATE_DATETIME_2
     , dbfluteRelno_2.UPDATE_USER AS UPDATE_USER_2
     , dbfluteRelno_2.UPDATE_PROCESS AS UPDATE_PROCESS_2
     , dbfluteRelno_2.WITHDRAWAL_REASON_CODE AS WITHDRAWAL_REASON_CODE_2
     , dbfluteRelno_2.WITHDRAWAL_REASON_INPUT_TEXT AS WITHDRAWAL_REASON_INPUT_TEXT_2
     , dbfluteRelno_2.WITHDRAWAL_DATETIME AS WITHDRAWAL_DATETIME_2
     , dbfluteRelno_2_1_n2.WITHDRAWAL_REASON_TEXT AS WITHDRAWAL_REASON_TEXT_2_1
     , dbfluteRelno_2_1_n2.DISPLAY_ORDER AS DISPLAY_ORDER_2_1
     , dbfluteRelno_2_1_n2.WITHDRAWAL_REASON_CODE AS WITHDRAWAL_REASON_CODE_2_1
  from MEMBER
    left outer join MEMBER_STATUS dbfluteRelno_0
      on MEMBER.MEMBER_STATUS_CODE = dbfluteRelno_0.MEMBER_STATUS_CODE
    left outer join MEMBER_WITHDRAWAL dbfluteRelno_2
      on MEMBER.MEMBER_ID = dbfluteRelno_2.MEMBER_ID
    left outer join WITHDRAWAL_REASON dbfluteRelno_2_1_n2
      on dbfluteRelno_2.WITHDRAWAL_REASON_CODE = dbfluteRelno_2_1_n2.WITHDRAWAL_REASON_CODE
 where MEMBER.MEMBER_ACCOUNT like 'M%'
 order by dbfluteRelno_0.DISPLAY_ORDER asc, MEMBER.MEMBER_BIRTHDAY desc, MEMBER.MEMBER_ID asc

次回

これにてConditionBeanの基本は一通り抑えました。実を言うと応用がたくさん存在するのですが,それらはまた後で説明させて頂きます。

次回は,いざというときに大活躍する「外だしSQL(OutsideSql⁠⁠」をみていきましょう!

編注)
次回は,2008年2月の公開予定です。

著者プロフィール

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

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