EJB3.1のContainer Managed Transactionでハマった

EJB3.1のContainer Managed Transaction(以下、CMT)を使用したWebアプリを動作させていたら、データソースのコネクションの開放漏れが原因で、コネクションプールがいっぱいになってしまう問題が発生しました。上記の問題が発生した原因は明白で、自分がEJB3.1とSpringのトランザクション管理機能を混同し、Connection.closeメソッドを呼ばなかったことです。

EJBトランザクション管理

前述のコネクション開放漏れが発生したことからもわかるように、EJBトランザクション管理がやってくれるのは、「トランザクション境界を出たタイミングでのコミット or ロールバック」までで、コネクションの開放はBean実装者の責務となるようです。

実際、「JSR 318: Enterprise JavaBeans3.1」のスペックの354ページには、CMT管理メソッド内部からCMT管理のコネクションのsetAutoCommit,commit,rollbackを実行してはならないと記述されていますが、closeメソッドについての記述が無く、次のページのサンプルでも、コミットせずにコネクションをクローズしています。(凄く不思議な感じですね・・・)


JSR-000318 Enterprise JavaBeans 3.1 Maintenance Release

Springのトランザクション管理

一方、Springではトランザクションマネージャの仕様として、コミット、ロールバックのタイミングでトランザクションをクリーンナップすることになっています。


Interface PlatformTransactionManager

少なくともデータソースを扱う場合は、DataSourceUtilsクラスを使ってリソースマネージャにトランザクション管理対象としてコネクションを登録し、トランザクションマネージャにDataSourceTransactionManagerを使用すればコミット、ロールバックのタイミングでコネクションが開放されます。また、Springが提供しているJdbcTemplateやSqlMapClientTemplateは、内部でDataSourceUtilsを使ってるので、Spring + MyBatisならば、コネクションの開放漏れが発生する心配は無さそうです。

まとめ

EJBの方が後発ということもあり、CoC、XMLレスが徹底していて記述するのは楽ですが、Javaの標準のAPI(JDBCJPA、JMS etc...)を扱うことしか考慮されておらず、簡単に拡張もできなさそうなので、痒いところに手が届かない感じがします。

前述の問題も、実際にコネクションを触っているのは、MyBatisが動的に生成したマッパークラスのインスタンスなのですが、コネクションが自動的に開放されないせいで、サービスクラスにPOJOのマッパークラスをCDIでインジェクションし、サービスクラスではインフラ層を意識させないという作りを変える必要が出てきました。少なくとも、明示的にリソースを開放する必要があるので、try-finally句が必須になってしまいますね。(いい方法があれば教えて欲しいです・・・)

一方、SpringはXMLの記述が面倒だし、クラス数が膨大で覚えることが非常に多いですが、トランザクションマネージャの仕組みが非常に柔軟だし、サードパーティライブラリのサポートも手厚く、AOPやSpring ELなど過剰なくらい強力なので何でもできるのは良いですね。(色々ありすぎるので、もっとシンプルなら良いのですが・・・)

Spring Frameworkとは編集