EntityManage はスレッドセーフではない

おそらく FA です.
id:taedium さん,id:da-yoshi さん,ご協力ありがとうございました.


Hibernate EntityManager のドキュメントには次のようにはっきりと書いてありました.

An EntityManager is an inexpensive, non-threadsafe object that should be used once, for a single business process, a single unit of work, and then discarded.

Hibernate EntityManager が堂々とスレッドセーフじゃないと言い切ってるってことは,Persistence API では EntityManager をスレッドセーフにすることを義務づけてはいないということでしょう.
id:taedium さんが引用してくれているように,実装側で好きなようにしていいらしい.
実際,仕様書には "thread" という用語は一度も使われていません.恐るべし.
っていうか,「EntityManager はスレッドセーフでなくてもいい」「EntityManager がスレッドセーフだと期待してはいけない」とか書いておいて欲しいなぁ.
そんなわけで (どんなわけで?),id:da-yoshi さんが挙げておられる getFlushMode() などがスレッドセーフでない Hibernate EntityManager 3.1beta6 の実装はまるっと問題なしということになりますね.


EntityManager はスレッドセーフではないので,singleton にすることはできません.
また,EntityManager を DI されるコンポーネント (Dao) なんかも singleton にしてはいけません.
という書き方はあまり正確じゃないですね.
singleton でも prototype でも何でもいいけど,複数のスレッドから同時に同じ EntityManager を使ってはいけません.再入禁止.
もっとも,DB アクセスを伴う EntityManager を同期して使うことはあり得ないので,結局 singleton 禁止.


で,アプリケーションコンポーネントはどうするか.
現実的な手段は昨日書いたように

  • Dao もサービスもみーんな prototype にする.
  • EntityManager ではなく,EntityManagerFactory を DI してもらう.

ってところでしょう.
他にはスレッドセーフな EntityManager のプロキシを提供するっていう手もなくはないけど...
PrototypeDelegateInterceptor 使えば簡単そうだけど...
普通なら間違ってる使い方が正しく動いちゃうのはかなりいやーんなのでボツ.


で,EJB3 的には前者 (みーんな prototype) なんだろうなぁ.
そもそも EntityManager なんかを DI してもらえるってことはそいつは EJB なわけで,そして EJB はなんたって再入不可なので,そもそもが singleton のような使い方はあり得ないわけで.


でもでも,実は Servlet にも @PersistenceContext なんかを書けるとかいう話があって (Servlet 2.5 Maintenance Review August 11, 2005 にはそんな記述無いけど),そして ServletEJB と違ってデフォルトでは再入可なわけで,ってことはどうするかっていうと後者 (EntityManagerFactory を使う) らしい.以下参照.
http://weblogs.java.net/blog/ss141213/
ってことは,EntityManagerFactory はスレッドセーフだと決めつけていいのだろうか?
Hibernate EntityManager の実装はスレッドセーフだと明記されているけど,Persistence API 仕様では義務づけられているようには見えないわけですが.
まぁいいや.


ともあれ (JW),この辺は Dao やサービスを EJB3 として作りたい (他の EJB3 コンテナでも動かしたい) のか,EJB3 なんてどうでもいいのかで違ってきそうですね.
EJB3 として作るなら最初から singleton はあり得ないから悩むことは何もなくて,Dao もサービスもみーんな Session Bean.結果的にみーんな prototype になるので EntityManager を DI してもらえばそれでよし.
EJB3 どーでもよければ prototype にして EntityManager を DI してもらうのも在^hありだし,singleton にして EntityManagerFactory を DI してもらうのも在^hあり.


いずれにせよ,S2Hibernate-JPA としては EntityManagerFactorysingletonEntityManagerprototype で提供するので,お好きなようにどうぞという感じ.