Seasar DI Container with AOP

to Top

Behaviorについて記述します。



// ======================================================================================================
//                                                                                               Behavior
//                                                                                               ========

★★★★★★★★★★★★★★★★★★★★★★
新デザインへの移行がまだできておりません。
申し訳ありませんが、しばらくお待ち下さい。
★★★★★★★★★★★★★★★★★★★★★★

# ----------------------------------------------------------
#                                          What is behavior?
#                                          -----------------

Behaviorとは

  「DaoとEntityの定番処理を行うObject」

  名前の由来は? → Daoの振舞い (The behavior of dao)
  その役割は?   → Data-HandlingのFacade (The facade of data handling)


S2DaoのDaoは実質的にSQLの実行しかできませんが、実際の業務アプリでは、
“整合性Check”“Paging処理”などのDataを扱った定番処理を行わなければなりません。
それらの定番処理をDBFluteで自動生成したものがBehaviorとなります。

他のO/R-Mapperで似た位置付けを挙げるならば、
「Hibernateで言うDao」、「Torqueで言うPeer」に相当します。


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
厳密な意味がどうでもいい人は、
「DBFluteではBehaviorを使って検索したり更新したりする」と覚えてもらうだけで構いません。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


# ----------------------------------------------------------
#                                      The merit of behavior
#                                      ---------------------

先に挙げた“Paging処理”を例にとって比較します。

Paging処理は一般的に以下のような処理になります。

    1. Paging条件無しの時の全件Record数の検索 → select count(*) from xxx
    2. Paging条件ありでの検索 → select ... from xxx limit 20 offset 40
    3. {1}と{2}の結果から総Page数や次Page存在判定などの計算

こういった処理の再利用を解決します。

  {Behaviorを利用しない}
    呼び出し側が{1}-{2}-{3}の処理を手続き的に行います。
    Daoを使って{1}と{2}を実行し、{3}の処理を呼び出し側が自分で行います。
    その辺の処理の再利用を自分で考える必要があります。

      ex) BOOK一覧のPaging検索
      /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
      final BookDao bookDao = [...DaoのInstanceを取得]
      final BookCB cb = new BookCB();
      [...ConditionBean(条件)の設定]
      cb.fetchFirst(20); // 1PageのSizeは20件
      cb.fetchFirst(3);  // 3Page目を検索したい
      final int allRecordCount = bookDao.selectCount(cb); // {1}の処理を実行
      final java.util.List<Book> bookList = bookDao.selectList(cb); // {2}の処理を実行
      [...結果から計算] // {3}の処理を実行 ※ここが大変(Bugも生みやすい)

      // Paging結果のHandling
      [...Pagingの結果情報がバラバラなので必要に応じて独自のDTOにまとめる] // ※ここも面倒である
      - - - - - - - - - -/

  {Behaviorを利用する}
    {1}-{2}-{3}の処理を自動生成された一行のMethod呼び出しで完了します。
    こういったApplication依存でない「DaoとEntityの定番処理」の再利用が既に解決されています。

      ex) BOOK一覧のPaging検索 -- selectPage()
      /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
      final BookBhv bookBhv = [...BehaviorのInstanceを取得]
      final BookCB cb = new BookCB();
      [...ConditionBean(条件)の設定]
      cb.fetchFirst(20); // 1PageのSizeは20件
      cb.fetchFirst(3);  // 3Page目を検索したい
      final PagingResultBean<Book> bookPage = bookBhv.selectPage(cb); // {1}-{2}-{3}の処理を実行

      // Paging結果のHandling
      [...単一ObjectのbookPageからPaging結果に関する全ての情報が取得できる]
      - - - - - - - - - -/


また、BehaviorもEntityやDaoと同様にGenerationGapになっているため、Application独自の処理を
実装することも可能です。該当のTableが持つ固有のLogic(Dataの振舞い)をBehaviorに実装し、
複数のProcess(画面やBatchなど)で利用するLogicの再利用を解決することが可能です。


# ----------------------------------------------------------
#                                        How to use behavior
#                                        -------------------

Behaviorは、dbflute.diconにてInterfaceを持たないClassとしてComponent登録されます。
Interfaceを持たないため、Setter-Injectionされるか否かはS2ContainerのVersionに依存します。

利用したいBehaviorのComponentNameと同名のSetterを用意すればInjectionされます。

    / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    public class Xxx {
        public void setBookBhv(BookBhv bookBhv) {
            this.bookBhv = bookBhv;
        }
        public void executeXxx() {
            final Book entity = Book();
            entity.setBookId(123);
            entity.setBookName("xxx");
            this.bookBhv.insertOrUpdate(entity);
        }
    }
    - - - - - - - - /


# ----------------------------------------------------------
#                   What kind of methods does behavior have?
#                   ----------------------------------------

Behaviorの用途別Method概要 をご覧下さい。



# ---------------------------------------------------------
#                               The other merit of behavior
#                               ---------------------------

BehaviorはS2Daoの“クセ”を隠蔽します。

冒頭で挙げた“整合性Check”というのがそれに該当します。


【ParameterのCheck】
Parameterに対する整合性Checkを掛けることができます。

  {Behaviorを利用しない}
    S2DaoのDaoは、Interfaceのみが存在していて、Methodが呼び出された後の処理に対して
    Applicationで手を加えることが一切できません。(やるとなったらInterceptorになってしまう)
    なので、ParameterのNullCheckなど呼び出し側が随時やる必要があります。

  {Behaviorを利用する}
    Behaviorで自動生成されたMethodは引数の厳密なCheckとDebug用Messageを格納した例外発生を行います。
    呼び出し側がMethod呼び出し前にCheckしたり、Debugしづらい例外が発生してしまうことはありません。


【戻り値の整合性Check】
1件を期待しているのに複数件HITしてしまった場合の扱いが違います。

  {Behaviorを利用しない}
    S2DaoのDaoの“戻り値がEntity型の1件検索Method(selectEntity())”は、
    S2Daoの仕様として「SQLが複数件数を返してきた場合は最初の1件を返す」となっています。
    実開発においては、「例外にして欲しい」と思うところです。
    (1件を期待しているのに複数件だったら既に条件かDataかどこかにBUGあるため)

  {Behaviorを利用する}
    1件を期待しているのに複数件だったら専用の例外になります。
    もし最初の1件を検索したい場合は、Paging機能で先頭の1件だけを指定して検索すれば実現できます。

      ex) BOOKの1件検索 -- selectEntity()
      /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
      final BookBhv bookBhv = [...BehaviorのInstanceを取得]
      final BookCB cb = new BookCB();
      [...ConditionBean(条件)の設定]
      final Book book = bookBhv.selectEntity(cb); // NullCheckが行われる。
      - - - - - - - - - -/

また、既に絶対1件HITすることが前提の検索Methodが存在します。

  {Behaviorを利用しない}
    また、S2DaoのDaoの“戻り値がEntity型の1件検索Method(selectEntity())”は0件HIT時にnullを返します。
    実開発においては、「既に絶対1件HITすることが前提」で検索することがかなり多いです。
    そのとき、呼び出し側がいちいち戻り値をNullCheckするのは面倒ですし、Checkせずに
    Data不備でnullが帰ってきて、NullPointerExceptionが発生するのも効率が悪いです。

  {Behaviorを利用する}
    0件取得時にDebug用Messageを格納した例外発生を行います。

      ex) BOOKの1件検索 -- selectEntityWithDeletedCheck()
      /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
      final BookBhv bookBhv = [...BehaviorのInstanceを取得]
      final BookCB cb = new BookCB();
      [...ConditionBean(条件)の設定]
      final Book book = bookBhv.selectEntityWithDeletedCheck(cb); // 戻り値のNullCheckが行われる。
      assertNotNull(book); // bookがnullになることは絶対にない
      - - - - - - - - - -/


これらはちょっとした実装補助ではありますが、10人20人と開発者が増えれば増えるほど効果を発揮します。
「このちょっとした実装補助が無いためにDebugに時間を掛けてしまった」なんてことは避けたいと考えます。
(呼び出し側はBehaviorのみ利用し、S2DaoのDaoを直接参照しないことを推奨)