Seasar DI Container with AOP

to Top

LimitSearch/PagingSearchについて記述します。


※DBFluteは、S2Pagerを参考にしていますがS2Pager自身を利用していません。


// ======================================================================================================
//                                                            ConditionBeanによるLimitSearch/PagingSearch
//                                                            ===========================================
// -----------------------------------------------------
//                                           LimitSearch
//                                           -----------

ConditionBeanにおいて、「先頭の何件を取得する」という条件の検索が可能です。

    ex) BOOKに対してAuthorが30歳以上/登録日時の降順/最初の50件のみ検索
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    final LdBookCB cb = new LdBookCB();
    cb.query().queryAuthor().setAuthorAge_GreaterEqual(30);
    cb.query().addOrderBy_RTime_Desc();
    cb.fetchFirst(50);
    final java.util.List<LdBook> ls = dao.selectList(cb);
    - - - - - - - - -/


また、「何件から何件を取得する」という条件の検索が可能です。

    ex) BOOKに対しAuthorが30歳以上/登録日時の降順/81件目から100件目のみ検索
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    final LdBookCB cb = new LdBookCB();
    cb.query().queryAuthor().setAuthorAge_GreaterEqual(30);
    cb.query().addOrderBy_RTime_Desc();
    cb.fetchScope(80, 20);// 80件目飛ばして20件を取得(81件目から100件目)
    final java.util.List<LdBook> ls = dao.selectList(cb);
    - - - - - - - - -/


[補足]
  {Oracle、FirstBird、MySQL、PostgreSQL}など、SQL文法的にLimitSearchがサポートされているDB
  に関しては、そのSQLを利用して検索します(ROWNUMやoffset/limitなど)。
  {SQLServer、DB2}など、SQL文法的に「先頭から何件」という検索しかできないDBに関しては、
  例えば80-100を取得する際に、先頭から100件を取得しResultSetで先頭80件を飛ばして取得します。

  ※Adviceを頂きまして、DB2に関しては、ROW_NUMBER()関数を利用したLimitSearchを
    検討します(2006/09/28現在)。


// -----------------------------------------------------
//                                          PagingSearch
//                                          ------------

ConditionBeanにおいて、「PageSizeを何件として指定したPageを検索する」という条件の検索が可能です。

    ex) BOOKに対してAuthorが30歳以上/登録日時の降順/PageSize20件でPaging検索
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    final LdBookCB cb = new LdBookCB();
    cb.query().queryAuthor().setAuthorAge_GreaterEqual(30);
    cb.query().addOrderBy_RTime_Desc();
    cb.fetchFirst(20);// PageSizeの設定
    cb.fetchPage(1);// 1-20
    final java.util.List<LdBook> lsFirstPage = dao.selectList(cb);
    cb.fetchPage(2);// 21-40
    final java.util.List<LdBook> lsSecoundPage = dao.selectList(cb);
    cb.fetchPage(3);// 41-60
    final java.util.List<LdBook> lsThirdPage = dao.selectList(cb);
    - - - - - - - - -/


// -----------------------------------------------------
//                                      PagingResultBean
//                                      ----------------

DBFluteには、Pagingの結果を管理するObjectが用意されています。
そのObjectを効率よく利用するMethodがBehaviorに存在します。

    ex) BOOKに対してAuthorが30歳以上/登録日時の降順/PageSize20件でPaging検索
        ※該当のConditionBeanにて条件に合致する総Record数が76件だと仮定します。
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    final LdBookCB cb = new LdBookCB();
    cb.query().queryAuthor().setAuthorAge_GreaterEqual(30);
    cb.query().addOrderBy_RTime_Desc();
    cb.fetchFirst(20);// PageSizeの設定
    cb.fetchPage(1);// 1-20
    final LdPagingResultBean<LdBook> rbFirstPage = bhv.selectPage(cb);
    cb.fetchPage(2);// 21-40
    final LdPagingResultBean<LdBook> rbSecondPage = bhv.selectPage(cb);
    cb.fetchPage(3);// 41-60
    final LdPagingResultBean<LdBook> rbThirdPage = bhv.selectPage(cb);
    cb.fetchPage(4);// 61-76
    final LdPagingResultBean<LdBook> rbFourthPage = bhv.selectPage(cb);

    // ***************************************
    // 「1」Page目のPaging結果についてのAssert
    // ***************************************

    // 検索したTable名(結合の中心Table) → BOOK
    assertEquals("BOOK", rbFirstPage.getTableDbName());

    // 総Record数 → 76
    assertEquals(76, rbFirstPage.getAllRecordCount());

    // 該当PageのEntityのList → 件数はPageSizeと同じ
    assertEquals(20, rbFirstPage.getSelectedList().size());

    // OrderByの一番目の要素のASC/DESC → DESC
    assertTrue(getOrderByClause().isFirstElementDesc());

    // OrderByの一番目の要素の列名 → R_TIME
    assertTrue(getOrderByClause().isSameAsFirstElementColumnName("R_TIME"));

    // PageSize → 20
    assertEquals(20, rbFirstPage.getPageSize());

    // 現在のPage番号 → 1
    assertEquals(1, rbFirstPage.getCurrentPageNumber());

    // 総Page数 → 4      ※allRecordCountとpageSizeから計算
    assertEquals(4, rbFirstPage.getAllPageCount());

    // 前のPageがあるか否か → 現在は1Page目なのでFalse
    assertFalse(rbFirstPage.isExistPrePage());

    // 次のPageがあるか否か → 次の2Page目があるのでTrue
    assertTrue(rbFirstPage.isExistNextPage());

    // ***************************************
    // 「2」Page目のPaging結果についてのAssert
    // ***************************************
    // ※共通部のAssertは省略{それらは1Page目と同じ値となります}

    // 現在のPage番号 → 2
    assertEquals(2, rbSecondPage.getCurrentPageNumber());

    // 前のPageがあるか否か → 現在は2Page目なのでTrue
    assertTrue(rbSecondPage.isExistPrePage());

    // 次のPageがあるか否か → 次の3Page目があるのでTrue
    assertTrue(rbSecondPage.isExistNextPage());

    // ***************************************
    // 「3」Page目のPaging結果についてのAssert
    // ***************************************

    // 現在のPage番号 → 3
    assertEquals(3, rbThirdPage.getCurrentPageNumber());

    // 前のPageがあるか否か → 現在は3Page目なのでTrue
    assertTrue(rbThirdPage.isExistPrePage());

    // 次のPageがあるか否か → 次の4Page目があるのでTrue
    assertTrue(rbThirdPage.isExistNextPage());

    // ***************************************
    // 「4」Page目のPaging結果についてのAssert
    // ***************************************

    // 現在のPage番号 → 4
    assertEquals(4, rbFourthPage.getCurrentPageNumber());

    // 該当PageのEntityのList → 最後のPageなので件数は16件
    assertEquals(16, rbFourthPage.getSelectedList().size());

    // 前のPageがあるか否か → 現在は4Page目なのでTrue
    assertTrue(rbFourthPage.isExistPrePage());

    // 次のPageがあるか否か → 次の5Page目はないのでFalse
    assertFalse(rbFourthPage.isExistNextPage());
    - - - - - - - - -/


もし総Page数が100件以上など膨大な場合に、画面にて全てのPage番号のLinkを表示するのはあまり格好良くありません。

    [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29...]

その場合に「ある限られたPage番号のみ」を表示することがよくあります。

PagingResultBeanは、「ある限られたPage番号」を算出するLogicを 2 Pattern用意しています。
(他にPatternがあるようでしたら相談して下さい)


{PageRange}

    [4]Page目を選択  - [1 2 3 4 5 6 7 8 9 次へ]
    [13]Page目を選択 - [前へ 8 9 10 11 12 13 14 15 16 17 18 次へ]
    [21]Page目を選択 - [前へ 16 17 18 19 20 21 22]
    ※最大22Pageまでとして

    ex) 「ある限られたPage番号」は現在のPage番号の前後1Pageとした場合
        ※一般的には、前後5Pageとか前後10Pageとかが基本です。
        ※これらは、設定されたPageRangeSizeとPaging結果を基に都度算出します。(PageRangeSizeの設定を忘れずに)
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // **********************
    // 「1」Page目のPageRange
    // **********************
    // PageRangeSizeは現在のPage番号の前後5Pageとする
    rbFirstPage.setPageRangeSize(5);

    // 該当するPageRangeのPage番号の配列
    final int[] expectedFirstArray = new int[]{1,2,3,4,5,6};
    assertEquals(expectedFirstArray, rbFirstPage.pageRange().createPageNumberArray());

    // 前のPageRangeがあるか否か → 1Page目より前はないのでFalse
    assertFalse(rbFirstPage.pageRange().isExistPrePageRange());

    // 次のPageRangeがあるか否か → 7Page目があるのでTrue
    assertTrue(rbFirstPage.pageRange().isExistNextPageRange());

    // **********************
    // 「4」Page目のPageRange
    // **********************
    // PageRangeSizeは現在のPage番号の前後5Pageとする
    rbSecondPage.setPageRangeSize(5);

    // 該当するPageRangeのPage番号の配列
    final int[] expectedSecondArray = new int[]{1,2,3,4,5,6,7,8,9};
    assertEquals(expectedSecondArray, rbSecondPage.pageRange().createPageNumberArray());

    // 前のPageRangeがあるか否か → 1Page目より前は無いのでFalse
    assertFalse(rbSecondPage.pageRange().isExistPrePageRange());

    // 次のPageRangeがあるか否か → 10Page目があるのでTrue
    assertTrue(rbSecondPage.pageRange().isExistNextPageRange());

    // **********************
    // 「13」Page目のPageRange
    // **********************
    // PageRangeSizeは現在のPage番号の前後5Pageとする
    rbThirdPage.setPageRangeSize(5);

    // 該当するPageRangeのPage番号の配列
    final int[] expectedThirdArray = new int[]{8,9,10,11,12,13,14,15,16,17,18};
    assertEquals(expectedThirdArray, rbThirdPage.pageRange().createPageNumberArray());

    // 前のPageRangeがあるか否か → 7Page目はあるのでTrue
    assertTrue(rbThirdPage.pageRange().isExistPrePageRange());

    // 次のPageRangeがあるか否か → 19Page目はあるのでTrue
    assertTrue(rbThirdPage.pageRange().isExistNextPageRange());

    // **********************
    // 「21」Page目のPageRange
    // **********************
    // PageRangeSizeは現在のPage番号の前後5Pageとする
    rbFourthPage.setPageRangeSize(5);

    // 該当するPageRangeのPage番号の配列
    final int[] expectedThirdArray = new int[]{16,17,18,19,20,21,22};
    assertEquals(expectedThirdArray, rbFourthPage.pageRange().createPageNumberArray());

    // 前のPageRangeがあるか否か → 15Page目はあるのでTrue
    assertTrue(rbFourthPage.pageRange().isExistPrePageRange());

    // 次のPageRangeがあるか否か → 23Page目以降はないのでFalse
    assertFalse(rbFourthPage.pageRange().isExistNextPageRange());
    - - - - - - - - -/

    _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    [4]Page目を選択  - [1 2 3 4 5 6 7 8 9 次へ]
    [13]Page目を選択 - [前へ 8 9 10 11 12 13 14 15 16 17 18 次へ]
    [21]Page目を選択 - [前へ 16 17 18 19 20 21 22]
    ※最大22Pageまでとして

    ではなく

    [4]Page目を選択  - [1 2 3 4 5 6 7 8 9 10 11 次へ]
    [13]Page目を選択 - [前へ 8 9 10 11 12 13 14 15 16 17 18 次へ]
    [21]Page目を選択 - [前へ 12 13 14 15 16 17 18 19 20 21 22]
    ※最大22Pageまでとして

    とする場合は以下のように指定することで実現可能です。

    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // **********************
    // 「1」Page目のPageRange
    // **********************
    // PageRangeSizeは現在のPage番号の前後1Pageとする
    final PageRangeOption option = new PageRangeOption();
    option.setPageRangeSize(1);
    option.setFillLimit(true);// ☆Point!
    rbFirstPage.setPageRangeSize(option);
    - - - - - - - - -/
    _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/


{PageGroup}

    [4]Page目を選択  - [1 2 3 4 5 6 7 8 9 10 11 次へ]
    [13]Page目を選択 - [前へ 11 12 13 14 15 16 17 18 19 20 次へ]
    [21]Page目を選択 - [前へ 21 22]
    ※最大22Pageまでとして

    ex) 「ある限られたPage番号」は2Page毎のGroupとする場合
        ※一般的には、5Page/10Pageとかが基本です。
        ※これらは、設定されたPageGroupSizeとPaging結果を基に都度算出します。(PageGroupSizeの設定を忘れずに)
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // **********************
    // 「1」Page目のPageGroup
    // **********************
    // PageGroupSizeは10Page毎のGroupとする
    rbFirstPage.setPageGroupSize(10);

    // 該当するPageGroupのPage番号の配列
    final int[] expectedFirstArray = new int[]{1,2,3,4,5,6,7,8,9,10};
    assertEquals(expectedFirstArray, rbFirstPage.pageGroup().createPageNumberList()));

    // 前のPageGroupがあるか否か → 1Page目より前はないのでFalse
    assertFalse(rbFirstPage.pageGroup().isExistPrePageGroup());

    // 次のPageGroupがあるか否か → 11Page目があるのでTrue
    assertTrue(rbFirstPage.pageGroup().isExistNextPageGroup());

    // **********************
    // 「4」Page目のPageGroup
    // **********************
    // PageGroupSizeは10Page毎のGroupとする
    rbSecondPage.setPageGroupSize(10);

    // 該当するPageGroupのPage番号の配列
    final int[] expectedSecondArray = new int[]{1,2,3,4,5,6,7,8,9,10};
    assertEquals(expectedSecondArray, rbSecondPage.pageGroup().createPageNumberList()));

    // 前のPageGroupがあるか否か → 1Page目より前はないのでFalse
    assertFalse(rbSecondPage.pageGroup().isExistPrePageGroup());

    // 次のPageGroupがあるか否か → 11Page目があるのでTrue
    assertTrue(rbSecondPage.pageGroup().isExistNextPageGroup());

    // **********************
    // 「13」Page目のPageGroup
    // **********************
    // PageGroupSizeは10Page毎のGroupとする
    rbThirdPage.setPageGroupSize(10);

    // 該当するPageGroupのPage番号の配列
    final int[] expectedThirdArray = new int[]{11,12,13,14,15,16,17,18,19,20};
    assertEquals(expectedThirdArray, rbThirdPage.pageGroup().createPageNumberList()));

    // 前のPageGroupがあるか否か → 10Page目より前があるのでTrue
    assertTrue(rbThirdPage.pageGroup().isExistPrePageGroup());

    // 次のPageGroupがあるか否か → 21Page目があるのでTrue
    assertTrue(rbThirdPage.pageGroup().isExistNextPageGroup());

    // **********************
    // 「21」Page目のPageGroup
    // **********************
    // PageGroupSizeは10Page毎のGroupとする
    rbFourthPage.setPageGroupSize(10);

    // 該当するPageGroupのPage番号の配列
    final int[] expectedFourthArray = new int[]{21,22};
    assertEquals(expectedFourthArray, rbFourthPage.pageGroup().createPageNumberList()));

    // 前のPageGroupがあるか否か → 20Page目より前があるのでTrue
    assertTrue(rbFourthPage.pageGroup().isExistPrePageGroup());

    // 次のPageGroupがあるか否か → 23Page目以降はないのでFalse
    assertFalse(rbFourthPage.pageGroup().isExistNextPageGroup());
    - - - - - - - - -/


// -----------------------------------------------------
//                           Behavior.selectPage()の補足
//                           ---------------------------

Behavior.selectPage()について

このMethodは、以下の処理を一手に引き受けてくれます。

  A. 総Record数取得のためのselectCount()の呼び出し
  B. 想定外Page数指定の際の再検索(すれ違いなど)
  C. Paging検索/PagingResultBeanの生成


{A}

総Record数を取得するために「LimitSearchの条件のみ除去したselectCount()」を実行します。

※総Page数を求めるために必ず必要です。


{B}

例えば、総Record数が61件で画面にて3Page目を開いてたとして、
4Page目に遷移する直前に他のThreadに4Page目に表示するはずの1件が削除されたとします。
(もしくは、現在の条件に合致しない値に更新されたとか)
すると4Page目で検索しても取得できるRecordは0件です。
その場合、selectPage()では、3Page目を検索し直します(その瞬間の最大Page)。

これは画面の非機能要件にあたる部分であり、各Projectによってやりたいことが変わるかもしれませんが、
Defaultでこのような動きをします。(大抵の場合はこの動きで良いのではないかと考えました)

特に、一覧の画面内にてRecordの編集・削除ができる場合に、その処理の後の再検索においてこの動きが利用できます。
4Page目を開いていて61件目を自分で編集・削除したことにより4Page目がなくなってしまう場合は、4Page目を再検索しても
3Page目が結果として帰ってきますので、Programが再検索のときにそこを意識する必要はありません。
(常に自Page番号で検索すれば良い)


もし、この動きではどうしても要件を満たせない場合は、selectPage()の別引数のMethodをご利用ください。

    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    selectPage(XxxCB cb, SelectPageInvoker<Xxx> invoker)
    - - - - - -/

SelectPageInvokerはInterfaceです。selectPage()はinvokeSelectPage()というMethodをCallbackします。
このMethodを自由に実装することにより、各Projectに合わせた動きを実現することが可能です。
(「A」に関しても同様のことが言えます)


{C}

Paging検索の実行と既に説明した通りのPaggingResultBeanを生成します。


_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
複雑な検索の場合は“外だしSQL”を利用します。そのやり方は後述します。

ただ、もしProjectにてViewを利用することができるのであれば、その複雑なSQLの基本部分をViewで実装すれば、
DBFluteはViewを一つのTableとして自動生成しますので、そのViewのConditionBeanでPagingが可能となります。

それが一番楽と言えば楽ですが、管理やMaintenance性を考えた場合にViewを積極利用しないProjectもあるかと思います。
その場合はやはり“外だしSQL”となります。
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/




// ======================================================================================================
//                                                            “外だしSQL”によるLimitSearch/PagingSearch
//                                                            ===========================================



★★★★★★★★★★★★★★★★★★★★★★★★★★★★
【お知らせ】
以下内容は将来削除される可能性があります。

リニューアルされたドキュメントがありますのでこちらをご覧下さい。
★★★★★★★★★★★★★★★★★★★★★★★★★★★★



Pagingで検索する画面は、複雑なSQLになりがちです。なので、外だしSQLでもPagingができなければなりません。


    ex) 呼び出し側
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SimplePagingBean pb = new SimplePagingBean();
    pb.fetchFirst(20);
    pb.fetchPage(4); // 81-100件目
    PagingResultBean<LdBook> rb = bhv.selectPageXxx(pb, xxxDate);
    - - - - - -/

        呼び出し側では、SimplePagingBeanを利用してPaging情報をBehaviorに渡すようにします。
        BehaviorのMethodはExに定義するProject独自のMethodとなります。



    ex) ExBehaviorの独自Method {例として'xxxDate'を絞込み条件のParameterとする}
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    public PagingResultBean<LdBook> selectPageXxx(final SimplePagingBean pb, final Timestamp xxxDate) {
        assertObjectNotNull("pb", pb);
        assertObjectNotNull("xxxDate", xxxDate);

        final SelectPageCallback<LdBook> callback = new SelectPageCallback<LdBook>() {
            public PagingBean getPagingBean() {
                return pb;
            }

            public int selectCountIgnoreFetchScope() {
                return getMyDao().selectCountXxx(xxxDate);
            }

            public List<LdBook> selectListWithFetchScope() {
                return getMyDao().selectPageXxx(pb, xxxDate);
            }
        };
        return new SelectPageSimpleInvoker<LdBook>(this).invokeSelectPage(callback);
    }
    - - - - - -/

        ExBehaviorに独自に作成する“外だしSQL”を利用したselectPage()を定義します。
        SelectPageCallbackのそれぞれの実装MethodにDaoのMethodを指定します。

        selectCountIgnoreFetchScope()は、FetchScope(Paging)をしない場合に
        該当の条件でHITする件数を取得するMethodを呼び出すようにします。

        selectListWithFetchScope()は、実際にPagingの条件を考慮して検索するMethodを呼び出すようにします。



    ex) ExDaoの独自Method {例として'xxxDate'を絞込み条件のParameterとする}
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    public int selectCountXxx(Timestamp xxxDate);

    public java.util.List<LdBook> selectPageXxx(SimplePagingBean pb, Timestamp xxxDate);
    - - - - - -/

        ExDaoに独自のMethodを定義します。
        selectCountXxx()にはPagingBeanの引数は不要です。(Pagingを考慮しない件数を取りたいため)
        SimplePagingBeanは、必ず第1引数に指定して下さい。

    ex) FetchScope(Paging)を考慮しないSelectCountのSQL
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    select count(*)
      from BOOK
     where ...(色々複雑な条件...)
    - - - - - -/

        FetchScope(Paging)をしない場合に該当の条件でHITする件数を取得するSQLを用意します。

    ex) PagingのSQL{Oracleの場合}
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    select *
      from (select book.*, rownum as rn
              from (select BOOK_ID, BOOK_NAME, ...
                      from BOOK
                     where ...(色々複雑な条件...)
                   ) book
           )
     where rn > /*$pb.pageStartIndex*/80
       and rn <= /*$pb.pageEndIndex*/100
    - - - - - -/

    ex) PagingのSQL{MySQLの場合}
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    select BOOK_ID, BOOK_NAME, ...
      from BOOK
     where ...(色々複雑な条件...)
     limit /*$pb.pageStartIndex*/80, /*$pb.fetchSize*/20
    - - - - - -/

    ex) PagingのSQL{SQLServerの場合}
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    select top /*$pb.pageEndIndex*/100 BOOK_ID, BOOK_NAME, ...
      from BOOK
     where ...(色々複雑な条件...)
    - - - - - -/

        Paging検索のSQLを用意します。
        DBによってPagingの方法が異なります。

    TODO: 書き途中...様々なDBの書き方を追加すること。&動的OrderByの利用方法



// --------------------------------------------------------
//                    ParameterBeanとSimplePagingBeanの連携
//                    -------------------------------------
    *** @Hint ***
    ParameterBeanをSimplePagingBeanの継承Classとして利用することが可能です。

      ex) --!BookCollectionStatisticPmb extends SPB!
          ※詳しくは、Tips: Sql2Entity をご覧下さい。

    ParameterBeanとSimplePagingBeanの連携を利用すると、ExBehaviorでの実装は以下のようになります。

    ex) ExBehaviorの独自Method
    /- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    public PagingResultBean<LdBook> selectPageXxx(final LdBookCollectionStatisticPmb pmb) {
        assertObjectNotNull("pb", pb);

        final SelectPageCallback<LdBook> callback = new SelectPageCallback<LdBook>() {
            public PagingBean getPagingBean() {
                // ☆ParameterBean自体がPagingBeanのInterfaceをImplementしているためこのままpmbを指定すればよい
                return pmb;
            }

            public int selectCountIgnoreFetchScope() {
                return getMyDao().selectCountXxx(pmb);
            }

            public List<LdBook> selectListWithFetchScope() {
                return getMyDao().selectPageXxx(pmb);
            }
        };
        return new SelectPageSimpleInvoker<LdBook>(this).invokeSelectPage(callback);
    }
    - - - - - -/