CallbackContext
CallbackContextとは?
おおよそ、このような感じ
DBFlute の Behavior のDBアクセスの処理の中で、共通的な処理を挟み込むためのクラスです。 内部的にはスレッドローカルを利用して、指定された処理をフレームワーク内部まで届けます。
このような Hook が用意されています。
- BehaviorCommandHook
- Behaviorの入り口メソッドに近いレイヤでの前後Hook (多目的)
- SqlFireHook
- BehaviorのSQL実行直前レイヤでの前後Hook (多目的)
- SqlLogHandler
- (SQL実行前に) "SQLのログ" に対するHook
- SqlResultHandler
- (SQL実行後に) "SQLの結果" に対するHook (Entityマッピング後)
- SqlStringFilter
- (SQL実行前に) "SQL文字列"に対するHook
多目的な Hook に加えて、特定のリソースを取り扱うための Handler, Filter が用意されています。 業務要件やHookタイミングの実装上の都合に合わせて、使い分けると良いでしょう。
基本的な使い方をベタベタな実装で学ぶ
スレッドローカルでの処理なので、挟み込むように設定します。とりあえず、ベタベタな実装を見てみましょう。 (説明のためのベタベタなので、実際はこのように書くことはないです: 詳しくは後述)
e.g. CallbackContextのベタベタな実装 @Java
CallbackContext.setBehaviorCommandHookOnThread(new BehaviorCommandHook() { // ここで設定して...
@Override
public void hookBefore(BehaviorCommandMeta meta) {
// ここで事前処理
}
@Override
public void hookFinally(BehaviorCommandMeta meta, RuntimeException cause) {
// ここで事後処理
}
});
try {
... = memberBhv.selectList(cb -> { // この処理の中でHookが実行される
cb.query()...
});
} finally { // 最後にちゃんと終了
CallbackContext.terminateLastBehaviorCommandHookOnThread();
// 1.1.9まではclearしかなかった。クリアだと多重登録したもの全部消えちゃう
//CallbackContext.clearBehaviorCommandHookOnThread();
}
実際には、横断的に適用したいことがほとんどなので、このように個別に設定することはないでしょう。 後で外したら付け足したりすることを想定すると、アプリの業務的なコードの中で意識で使うべきではなく、アプリの仕組み部分の中で解決されるようにするのが現実的です。 (例えば、webフレームワークの横断的な前後処理を入れられるInterceptorに相当する部分などにおいて)
多重登録もできるけどclearに注意
同じ種類のインターフェースを複数回登録できます。 例えば、BehaviorCommandHook のインスタンスを二つ作って、二つとも set した場合、二つとも実行されます。
フレームワーク側(外側)で、すでに BehaviorCommandHook を利用しているときでも、アプリ側(内側)で独自の Hook を set することができます。
(@since 1.2.0) finallyでの終了は、terminateLast...() を使いましょう。同じ階層(レイヤ)でsetしたものだけを終了させることができます。 これを使うことで、階層(レイヤ)をあまり気にせず多重登録することができます。 とはいえ、あまりsetする箇所を散在させるのはわかりづらさを生むので、(多重登録は単純に部品化のための機能と捉えて)可能な限り一元管理して、アプリ(内側)ではその部分をオーバーライドで拡張して処理を付け足すように実装したほうが良いでしょう。
(@until 1.1.9) ただし、clearするとすべて一括でクリアされるので、もしレイヤごとに段階的に set していくのであれば、フレームワーク側(外側)で必ずクリアされるようにしておいて、アプリ側(内側)ではクリアはしないようにすると良いでしょう。 だがしかし、多重登録できるとはいえ、そもそも CallbackContext の設定箇所は一元管理する方が良いです(多重登録は単純に部品化のための機能と捉えて)。 もしフレームワーク側で利用しているのであれば、アプリでその部分をオーバーライドで拡張して処理を付け足すように実装したほうが良いでしょう。
BehaviorCommandHook
BehaviorCommandとは?
Behavior の selectList() や update() などのそれぞれのDBアクセスメソッドに対応する処理を BehaviorCommand と呼び、DBFlute内部ではそれぞれのメソッドに対応する処理を BehaviorCommand インターフェースで抽象的に取り扱うようにしています。
BehaviorCommandHookの基本情報
Behaviorの入り口メソッドに近いレイヤでの前後Hookで、多目的なインターフェースです。
- 用途
- 多目的
- 実行レイヤ
- Behaviorの入り口メソッド (selectList()など) に近いところ
- 実行タイミング
- DBアクセス処理の実行前と実行後、例外発生時も実行後に含む
- コールバック引数
- BehaviorCommandMeta, 実行されたBehaviorやCBの情報など取得できる
Behavior自体がDIコンテナのDIコンポーネントなので、DIコンテナのAOP機能で前後処理を実現することもできますが、AOPはクラスエンハンスの負荷が発生しやすいので、DBFluteの仕組みだけで前後処理を実現できるようにしています。 (Behaviorに関しては、ダメでもはないけどできるだけ Interceptor は仕掛けたくない)
BehaviorCommandMetaとは?
BehaviorCommandHookのコールバック引数として、BehaviorCommandMetaインターフェースが利用できます。 該当テーブルの DBMeta (テーブルのメタデータ) や、検索系なのか?更新系なのか?などの判定ができます。