前回のConnectionの維持編でDB接続を維持するには、ThreadLocalを使えばいいのでは?という考察をしました。ThreadLocalは同一スレッド内でのグローバル変数的なものなので、他スレッドには影響を与えずに、リクエスト~レスポンスまでDB接続を維持し続けることができます。今回はその実装方法について考えて見ます。
サンプルプログラムは最後で提供しています。
実装案
以下の3つの仕組みを使います。
- ServletFilterによる「入り口と出口のDB接続管理」
- ThreadLocalによる「DB接続の維持」
- ExceptionHandlerによる「例外発生時のロールバック」
ServletFilterはServlet APIが提供する技術で、ServletやJSPが動作する前後にフィルタ的に処理を行うことができます。
つまりStrutsのActionServletが動作する直前と、JSPでのレンダリング後にを処理を行うことができます。最初と最後に動かすことができるのがFilterの特徴です。今回は終了処理でCommitやRollbackを行いDB接続を切断します。最終出口で全て処理してくれるので、アプリケーション内ではDB接続を切断する必要がありません。
ServletFilterの設定はweb.xmlで行うので注意してください。
ThreadLocalは前回説明したとおり、スレッド内で維持される変数です。
ここにはDB接続を保持します。スレッド内で最初にDB接続が必要になったときに、ThreadLocalに保持するようにします。以後は保持されたDB接続が得られるようになります。
ServletFilterの開始処理で、DB接続を作成しても間違いではないのですが、以後の処理でまったく使わない可能性もあるので、初めに取得しようとしたときに、DataSourceから取得します。
またCommitするかRollbackするかのフラグもThreadLocalで同様に持つことにします。アプリケーション内でCommitするかどうかの判断をするのではなく、原則としては例外発生時にはRollback。そうでなければCommitと決めうちします。したがって何も例外が発生しなければ、「Commitする」のまま、Filterまで継続されることになります。
ExceptionHandlerはStrutsが提供する例外ハンドリング機構です。
struts-configで定義することでAction内での例外発生時に処理を呼び出してくれます。(catchしてそのままにすると当然呼び出されません)システム全体の例外をハンドリングするならば、struts-configの<global-exceptions>タグで、java.lang.Exceptionをハンドリングすれば、全ての例外に対して処理することができます。
ExceptionHandlerが行う仕事は、CommitするかRollbackするかのフラグを、「Rollbackする」にすることです。これをThreadLocalに保持します。また例外発生時のログ出力などもここで行うことができます。
以上の実装方法で、リクエストからレスポンスまでを1トランザクションとし、その間のDB接続をどこからでも得ることができました。
この実装方法で、うまく処理できると考えているのですが、なにかマズい点があったらコメントいただけると勉強になります。
サンプルプログラム
提供しているzipファイルには3つのWebアプリケーションが入っていますが、今回の記事のサンプルプログラムはStruts-DB3です。(前回、前々回の記事も同梱してあります。)サンプルプログラムでは、テーブルの編集画面をデモとし、ServletFilter, ThreadLocal, ExceptionHandlerの3つを実装しています。データベースを使用しているのでHSQLDBを同梱してあります。動作させる場合にはTomcatなどのアプリケーションサーバーに対して、DataSourceの設定が必要です。(jdbc/sample3でlocalのHSQLDBを参照しています)