EntityManagerは複数のクラスで次のように@Inject[ed]スレッドセーフですか?
@PersistenceContext(unitName="blah")
private EntityManager em;
驚いたことに(何年も春にjpaを使用した後)、スレッドセーフではありません。これは、深く考えれば理解できます。これは、ネイティブ JPA 実装の単なるラッパーです。たとえば、Hibernate のセッションは、jdbc接続のラッパーです。それは、1 つのデータベース接続/トランザクションを表すため、スレッドセーフではありません。EntityManager
EntityManager
EntityManager
では、なぜSpringで機能するのでしょうか。ターゲットをプロキシでラップするためEntityManager
、原則としてThreadLocal
各スレッドごとにローカル参照を保持するために使用します。これは、Spring アプリケーションがシングルトンの上に構築され、EJB がオブジェクト プールを使用するために必要です。
そして、あなたの場合、どのように対処できますか?cdi はわかりませんが、EJB ではステートレスおよびステートフルの各セッション Bean がプールされます。つまり、複数のスレッドから同時に同じ EJB のメソッドを実際に呼び出すことはできません。したがってEntityManager
、同時に使用されることはありません。そうは言っても、少なくともステートレスおよびステートフル セッション Bean への注入EntityManager
は安全です。
ただし、サーブレットとシングルトン Bean への注入EntityManager
は、複数のスレッドが同時にアクセスして同じ JDBC 接続を台無しにする可能性があるため、安全ではありません。
EntityManagerの実装自体はスレッドセーフではありませんが、Java EEコンテナは、すべてのメソッド呼び出しをトランザクションにバインドされたEntityManagerに委任するプロキシを挿入します。したがって、各トランザクションはそれ自体のEntityManagerインスタンスで機能します。これは、少なくともトランザクションスコープの永続コンテキスト(デフォルト)に当てはまります。
コンテナが各BeanにEntityManagerの新しいインスタンスを注入する場合、以下は機能しません。
@Stateless
public class Repository1 {
@EJB
private Repository2 rep2;
@PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
@TransactionAttribute
public void doSomething() {
// Do something with em
rep2.doSomethingAgainInTheSameTransaction();
}
}
@Stateless
public class Repository2 {
@PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
@TransactionAttribute
public void doSomethingAgainInTheSameTransaction() {
// Do something with em
}
}
doSomething-> doSomethingAgainInTheSameTransaction呼び出しは単一のトランザクションで発生するため、Beanは同じEntityManagerを共有する必要があります。実際には、それらは同じ永続コンテキストへの呼び出しを委任する同じプロキシEntityManagerを共有します。
したがって、以下のようなシングルトンBeanでEntityManagerを合法的に使用できます。
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Repository {
@PersistenceContext(unitName="blah", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
}
もう1つの証拠は、EntityManagerjavadocにスレッドセーフについての言及がないことです。したがって、Java EEコンテナ内にいる間は、 EntityManagerへの同時アクセスを気にする必要はありません。
私の最初の答えは完全に真実ではなかったので、これについてさらに深く掘り下げる必要があると感じています。
JSR-220 (EJB 3.0)を参考にします。セクション5.2 EntityManager の取得では、以下を見つけることができます。
エンティティ マネージャは、同時に実行されている複数のスレッド間で共有することはできません。エンティティ マネージャには、シングル スレッドでのみアクセスできます。
それだけです。ここで読むのをやめて、適切に同期しない限り、シングルトン Bean でEntityManagerを使用しないでください。
しかし、仕様に混乱があると思います。実際には、2 つの異なるEntityManager実装があります。1 つ目は、スレッドセーフである必要のないプロバイダーの実装 (Hibernate と言います) です。
一方、EntityManagerのコンテナ実装があります。上記によると、これもスレッドセーフであるとは想定されていません。ただし、コンテナーの実装はプロキシとして機能し、すべての呼び出しを実際のプロバイダーのEntityManagerに委任します。
したがって、5.9 Runtime Contracts between the Container and Persistence Providerの仕様の詳細:
トランザクション スコープの永続コンテキストの管理では、JTA トランザクションに関連付けられている EntityManager がまだ存在しない場合: コンテナーは、Persistence-ContextType.TRANSACTION を使用したエンティティ マネージャーの最初の呼び出しが発生したときに、EntityManagerFactory.createEntityManager を呼び出して新しいエンティティ マネージャーを作成します。 JTA トランザクションで実行されるビジネス メソッドのスコープ内。
これは、開始されたトランザクションごとに異なるEntityManagerインスタンスが存在することを意味します。EntityManagerを作成するコードは、5.3に従って安全です。
EntityManagerFactory インターフェースのメソッドはスレッドセーフです。
しかし、JTA トランザクションに関連付けられたEntityManagerがある場合はどうなるでしょうか。現在の JTA トランザクションに関連付けられたEntityManagerをバインドするコードは、仕様によるとスレッドセーフではない可能性があります。
しかし、シングルトン内では正しくなく、ステートレス Bean に注入されたEntityManagerで正しく動作するアプリケーション サーバーの実装が思いつきません。
したがって、私の結論は次のとおりです。