2007-06-11

Springで JPAの共有EntityManagerを利用する

EntityManagerFactoryにさえアクセスできるようセットアップされていれば、いちおう JPAが利用出来ることになるのだが、毎度ファクトリから EntityManagerを取り出して使い終わったらクローズするといった処理の記述を省略できればさらに良い。

Java EEでは @PersistenceContext アノテーション (javax.persistenceパッケージにある) を用いることによって、コンテナから Beanに対して自動的に EntityManager (Factoryじゃなく、さっそく使える方)をインジェクトしてもらえるらしい(ちゃんと調べていないが多分そう)。これならファクトリから EntityManagerのインスタンスをもらったり、使った後に閉じたりする処理を記述しなくて済む分プログラムが少し短くできる(どれほどのもんよ?という疑問はあるが)。

実はこの機能、SpringでもSharedEntityManagerBeanとPersistenceAnnotationBeanPostProcessorの組み合わせで実現できるらしいので試してみた。

Bean定義ファイルに追加する内容

<bean id="entityManager"
class=
"org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

EntityManagerをインジェクトされる側

private EntityManager entityManager;
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
インジェクトされる側に必要なのはアノテーションのみで、Bean定義ファイルに property定義をする必要はない模様。

以上で EntityManagerがインジェクトされてくるようになるのを確認できた。
それって本当にスレッドセーフなのか?という疑問が当然のごとく湧いてくるかと思うが、そのあたりは SharedEntityManagerBeanや AbstractEntityManagerFactoryBeanがうまくやっているに違いないと信じている。

とりあえず JPAのAPIを通じて進行中のトランザクションに干渉してみるのはどうだろう、と考え、entityManager.getTransaction().setRollbackOnly() を試してみたところ、あえなく拒否された(それは使えないから SpringのPlatformTransactionManagerを使え、といったメッセージで)。

OpenJPAでの注意事項


上記のようにしてインジェクトされてきたEntityManagerをトランザクションの外で使おうとすると OpenJPAが例外をスローする。
org.apache.openjpa.persistence.InvalidStateException: The context has been closed. The stack trace at which the context was closed is available if Runtime=TRACE logging is enabled.
従って、共有EntityManagerを使う際は宣言的もしくはプログラム的トランザクションの内側で行うこと。

ソースコードがSpring依存にならない Keep POJOな 宣言的トランザクション
ソースコードはSpring依存になるが細かい制御の可能なプログラム的トランザクション

なおOpenJPA以外のJPAプロバイダではどうなるか試していない。

ラベル: ,

0 件のコメント:

コメントを投稿

<< ホーム