27

Is it safe to do something like this with CDI?

@Named
@ApplicationScoped
public class DAO {

   @PersistenceContext
   private EntityManager entityManager;

}

I understand that EntityManager itself is not thread-safe, and therefore should not be used in a shared global context like @ApplicationScoped. However, since the injected object with @PersistenceContext is actually a thread-aware wrapper around an underlying EntityManager, does that make this ok?

I've seen other posts on the subject but haven't been able to figure out an authoritative answer for this specific case. For example:

Java CDI @PersistenceContext and thread safety

It looks like it's safe to use with @Stateless, for instance - but I'm not sure if that's because of the way @Stateless works, or because of something intrinsic to @PersistenceContext itself.

EDIT The source of my confusion is that the @PersistenceContext injected EntityManager wrapper seems to be aware of the current thread, in order to figure out whether there's already a transaction in progress. So perhaps I'm confusing thread-awareness with thread-safety and they're two different things.

4

2 に答える 2

26

この場合、CDI はエンティティ マネージャーのコンテキスト プロキシを作成しないと確信しています。結局のところ、それはどのような範囲でしょうか?仮説に似たもの@ThreadScopedや単なるが必要な場合がありますが@RequestScoped、それ@PersistenceContextは CDI アノテーションではなく、CDI はそのセマンティクスを変更しません。

ここで起こっているのは、Java EE 6 プラットフォームの「マネージド Bean」インジェクションです。これは、サーブレットにエンティティ マネージャーをインジェクトするのと似ています。どちらの場合も、スレッドセーフではないインスタンスを直接使用することができます。

たとえば、@Stateless で安全に使用できるように見えますが、それが @Stateless の仕組みによるものなのか、それとも @PersistenceContext 自体に固有のものによるものなのかはわかりません。

それは働き方のせいです@Stateless。ステートレス Bean のメソッドへのすべての要求 (呼び出し) は、コンテナーによって一意のインスタンスにルーティングされます。コンテナーは、同じ Bean で 2 つのスレッドがアクティブにならないことを保証します。

CDI を使用すると、エンティティ マネージャーをリクエスト スコープの Bean にカプセル化し、それをアプリケーション スコープのビーンに注入することで、リクエストごとに同様の効果を得ることができます。

import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@RequestScoped
public class EntityManagerProvider {

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }

}

これを、以前にエンティティ マネージャーを注入した Bean に注入します。

@Named
@ApplicationScoped
public class DAO {

   @Inject
   private EntityManagerProvider entityManagerProvider;

}

これにより、リクエストごとに一意のエンティティ マネージャーが提供されます。これも簡単にプロデューサー メソッドに変換できるためgetEntityManager()、注入されたプロバイダーを呼び出す必要はありません。

于 2012-12-15T14:47:31.707 に答える