Springで JPAの共有EntityManagerを利用する
EntityManagerFactoryにさえアクセスできるようセットアップされていれば、いちおう JPAが利用出来ることになるのだが、毎度ファクトリから EntityManagerを取り出して使い終わったらクローズするといった処理の記述を省略できればさらに良い。
Java EEでは @PersistenceContext アノテーション (javax.persistenceパッケージにある) を用いることによって、コンテナから Beanに対して自動的に EntityManager (Factoryじゃなく、さっそく使える方)をインジェクトしてもらえるらしい(ちゃんと調べていないが多分そう)。これならファクトリから EntityManagerのインスタンスをもらったり、使った後に閉じたりする処理を記述しなくて済む分プログラムが少し短くできる(どれほどのもんよ?という疑問はあるが)。
実はこの機能、SpringでもSharedEntityManagerBeanとPersistenceAnnotationBeanPostProcessorの組み合わせで実現できるらしいので試してみた。
以上で EntityManagerがインジェクトされてくるようになるのを確認できた。
それって本当にスレッドセーフなのか?という疑問が当然のごとく湧いてくるかと思うが、そのあたりは SharedEntityManagerBeanや AbstractEntityManagerFactoryBeanがうまくやっているに違いないと信じている。
とりあえず JPAのAPIを通じて進行中のトランザクションに干渉してみるのはどうだろう、と考え、entityManager.getTransaction().setRollbackOnly() を試してみたところ、あえなく拒否された(それは使えないから SpringのPlatformTransactionManagerを使え、といったメッセージで)。
上記のようにしてインジェクトされてきた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プロバイダではどうなるか試していない。
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;インジェクトされる側に必要なのはアノテーションのみで、Bean定義ファイルに property定義をする必要はない模様。
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
以上で 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 件のコメント:
コメントを投稿
<< ホーム