19

Guice Persistを使用して、プロジェクトに EntityManager を挿入します。

例えば

public class MyDao{
   @Inject
   EntityManager em;

   public void someMethod(){
       //uses em instance
   }
}

EntityManagerしかし、注入された のインスタンスがどのように使用されようとしているのかは不明です。

  1. これはどのタイプの EntityManager ですか? (例:エンティティ マネージャのタイプを参照) 内部では、Guice Persist がそれをインスタンス化するEntityManagerFactory.createEntityManager()ため、アプリケーション管理のエンティティ マネージャと言えます。しかし、公式の Wikiでは、トランザクションごとのシード戦略について書いています。これは、EntityManager が (疑似) トランザクション スコープであることを示唆しています。
  2. 手動で close() を呼び出す必要がありますか? それとも Guice が処理しますか?
  3. 一次キャッシュの範囲は? EntityManager(トランザクション スコープのエンティティ マネージャーのように) 単一のトランザクションのみか、または(アプリケーション管理のエンティティ マネージャーのように)の同じ注入されたインスタンスを使用する限り?
4

4 に答える 4

30

質問は Piotr によって完全に回答されていますが、guice-persist の使用方法に関する実用的なアドバイスを追加したいと思います。

デバッグがかなり難しい問題がありました。私のアプリケーションでは、特定のスレッドが古いデータを表示し、EntityManagerインスタンスが古いデータベース接続のままになることがありました。根本的な原因は、@Transactionalアノテーションの使用方法にありました (読み取り専用メソッドではなく、更新/挿入/削除を行うメソッドにのみ使用しました)。guice-persistは、注入されたインスタンスを呼び出すとすぐにEntityManagerインスタンスを に保存します (これは読み取り専用メソッドに対して行いました)。ただし、これは、呼び出しも行う場合にのみ削除されます(通常、これは、次の場合にインターセプターによって行われます)。ThreadLocalget()Provider<EntityManager>ThreadLocalUnitOfWork.end()@Transactionalメソッドにあります)。そうしないと、EM インスタンスが ThreadLocal に残されるため、最終的にはスレッド プール内のすべてのスレッドが古いキャッシュ エンティティを持つ古い EM インスタンスを持つことになります。

したがって、次の単純なルールに固執する限り、guice-persist の使用法は簡単です。

  1. 直接注入Provider<EntityManager>するのではなく、必ず注入してください。EntityManager
  2. トランザクション スコープのセマンティクスの場合: 常に各メソッドに注釈を付けます@Transactional(読み取り専用メソッドであっても)。このようにJpaLocalTxnInterceptorして、アノテーション付きメソッドへの呼び出しをインターセプトし、トランザクションを開始してコミットするだけでなく、ThreadLocal で EM インスタンスを設定および設定解除します。
  3. リクエスト スコープのセマンティクスの場合: PersistFilterguice-persist に同梱されているサーブレット フィルターを使用します。リクエストが完了する前後に and を呼び出して、ThreadLocal にデータを取り込み、クリーンアップbegin()end()ます。UnitOfWork

お役に立てれば!

于 2014-02-10T14:30:22.173 に答える
12

Guice-persist のソース コードを調査し、Guice-persist wiki ページのコメントを読んだところ、次のような答えが必要でした。

1. @Inject EntityManager を介して注入された場合、EntityManager のライフサイクル管理は壊れています。Wiki のコメントの 1 つで述べたように:

プロバイダーの代わりに EntityManager を直接注入することは危険である可能性があることを確認しました。UnitOfWork または @Transaction で注釈が付けられたメソッドの内部にいない場合、スレッドでの EntityManager の最初の注入により、新しい EntityManager が作成され、破棄されることはなく、常にこの特定の EntityManager がこのスレッドに使用されます (EM はスレッドに格納されます-ローカル)。これは、死んだentityManagerの注入(接続が閉じられたなど)のようなひどい問題につながる可能性があります。したがって、常にプロバイダーを注入するか、少なくとも開いているUnitOfWork内にのみEntityManagerを直接注入することをお勧めします。

したがって、私の質問の例は、最も正しい使用法ではありません。EntityManager のシングルトン インスタンスを (スレッドごとに) 作成し、このインスタンスをどこにでも挿入します :-(.

ただし、Provider を注入して @Transactional メソッド内で使用した場合、EntityManager のインスタンスはトランザクションごとになります。したがって、この質問に対する答えは次のとおりです。正しく挿入されて使用された場合、エンティティ マネージャーはトランザクション スコープです。

2. 挿入して正しく使用すれば、手動でエンティティ マネージャーを閉じる必要はありません(guice-persist が処理してくれます)。誤って使用した場合、手動で閉じることは非常に悪い考えです ( @Inject EntityManager を実行すると、EntityManager の閉じたインスタンスがすべての場所に挿入されます)。

3. 挿入されて正しく使用された場合、L1 キャッシュの範囲は単一のトランザクションになります。誤って使用すると、L1 キャッシュのスコープはアプリケーションの有効期間になります (EntityManager はシングルトンです)。

于 2013-08-12T20:59:56.243 に答える
6

1. モジュール構成によって異なります。いくつかの基本的なバインディングがあります:

JpaPersistanceService

public class JpaPersistanceService implements Provider<EntityManager> {

  private EntityManagerFactory factory;

  public JpaPersistanceService(EntityManagerFactory factory) {
    this.factory = factory;
  }

  @Override
  public EntityManager get() {
    return factory.createEntityManager();
  }
}

モジュールバインディング

EntityManagerFactory factory = Persistence.createEntityManagerFactory(getEnvironment(stage));
bind(EntityManager.class).annotatedWith(Names.named("request")).toProvider(new JpaPersistanceService(factory)).in(RequestScoped.class);
bind(EntityManager.class).annotatedWith(Names.named("session")).toProvider(new JpaPersistanceService(factory)).in(SessionScoped.class);
bind(EntityManager.class).annotatedWith(Names.named("app")).toProvider(new JpaPersistanceService(factory)).asEagerSingleton;

使用法

@Inject @Named("request")
private EntityManager em; //inject a new EntityManager class every request

@Inject @Named("session")
private Provider<EntityManager> emProvider; //inject a new EntityManager class each session
//This is little bit tricky, cuz EntityManager is stored in session. In Stage.PRODUCTION are all injection created eagerly and there is no session at injection time. Session binding should be done in lazy way - inject provider and call emProvider.get() when em is needed;

@Inject @Named("application")
private EntityManager em; //inject singleton

2. はい、JpaPersistModule [javadoc]を使用する必要があります。

3.さて、これはpersistence.xmlとEntityManagerスコープのJPA構成についてです

于 2013-08-12T11:26:49.130 に答える
0

プロバイダーを注入しています....しかし、何かがおかしいと思います。アプリケーションを再デプロイしようとすると、JPA クラスがキャッシュされるため、常にサーバーを再起動する必要があります。

以下の疑似バグが発生します

https://bugs.eclipse.org/bugs/show_bug.cgi?id=326552

理論的には、プロバイダーを注入して EntityManager のインスタンスを取得することにより、何も閉じないでください....

于 2015-02-18T14:52:44.787 に答える