3

Spring doc を読んだところ、次のように書かれています。

@PersistenceContext アノテーションにはオプションの属性タイプがあり、デフォルトは PersistenceContextType.TRANSACTION です。このデフォルトは、共有 EntityManager プロキシを受け取るために必要なものです。

  1. これは、トランザクションで EntityManager を機能させる必要があるということですか?
  2. 以下のコードの loadProductsByCategory など、非トランザクション メソッド (読み取りクエリ) ではどのように機能しますか?
  3. 「共有」とはどういう意味ですか?他のユーザーと共有する EntityManager をどのように使用できますか?
  4. EntityManager をスレッドにバインドするには、メソッド loadProductsByCategory に @Transactional を追加する必要がありますか? クラス ProductDaoImpl はシングルトンでマルチスレッドで動作しますが、entityManager はスレッドセーフではないためです。

    @Service
    public class ProductDaoImpl implements ProductDao {
        @PersistenceContext
        private EntityManager em;
        public Collection loadProductsByCategory(String category) {
            Query query = em.createQuery("from Product as p where p.category = :category");
            query.setParameter("category", category);
            return query.getResultList();
        }
        @Transactional
        public void loadProductsByCategory(Product product) {
            em.persist(product);
        }
    }
    
4

1 に答える 1

8

この動作の詳細な分析は、次のブログ リンクにあります。http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/これが私の要約です。

EntityManagerSpring がインターフェースの独自の実装を提供できるようにする Java インターフェースです。Spring によって注入された実装は、動的プロキシを使用してエンティティ マネージャーへの呼び出しを処理します。動的プロキシは次のように動作します。

@TransactionalSpring のようにアノテーションがない場合、呼び出されたときに emloadProductsByCategoryのインスタンスが作成されます。Spring は JPA によって作成された Query オブジェクトを返しませんが、Spring を返します。この Spring プロキシのプロキシはすべての呼び出しを実際の実装に転送し、待機します。またはまたはが呼び出されるまで、すぐにエンティティ マネージャーを閉じます。EntityManagerem.createQueryEntityManagerQuerygetResultgetSingleResultexecuteUpdate

そのため、Springがない場合@Transactional、エンティティ マネージャができるだけ早く、つまり、エンティティ マネージャで各メソッドが呼び出された後、または結果セットが抽出された後に確実に閉じられます。上記の例では、query.getResultList() をコメントアウトすると、閉じられないエンティティ マネージャー インスタンスがリークしてしまいます。

 public Collection loadProductsByCategory(String category) {
        Query query = em.createQuery("from Product as p where p.category = :category");
        query.setParameter("category", category);
        return null;
        // a leak of an entity manager will happen because getResultList() was never called, so 
        // spring had no chance to close the entity manager it created when em.creaueQuery was 
        // invoked.
        // return query.getResultList(); 
 }

@Transactional 属性がある場合、Spring トランザクション マネージャーは、トランザクション メソッドが呼び出される前にトランザクション コンテキストを作成します。トランザクション メソッドがエンティティ マネージャーのメソッドを呼び出すと、Spring は新しい EntityManager インスタンスを作成し、それを現在のトランスナショナルに関連付けます。トランザクション メソッドが別のメソッドを呼び出し、別のメソッドが別のメソッドを呼び出し、それらすべてのメソッドがエンティティ マネージャーを使用する場合、エンティティ マネージャーはこれらすべての通話で共有されます。ここに例があります。

main(..)
{
    Foo foo = call spring to get foo instance 
    foo.doFoo();
} 

public class Foo {
     @PersistenceContext 
     EntityManager em; 

     @Autowired
     Bar bar;

     @Transactional 
     public doFoo(){
         // before this method is called spring starts a spring transaction
         em.createQuery(....) // here spring will create an instance of the Entity manager 
          // and assoicated with the current tx 
         bar.doBar(); // call bar transactional method 
     }
}

public calss Bar {

      @PersistenceContext 
     EntityManager em; 


     @Transactional 
     public doBar(){
         // no tx is started here because one was already started in doFoo
         em.createQuery(....) // spring looks under the current tx and finds that it has 
            // an entity manager, was created in the doFoo() method so this entity manager
            // is used, This is what is meant by sharing of the entity manager.
     }
}

最後の最後の質問に答えるために。

EntityManager をスレッドにバインドするには、メソッド loadProductsByCategory に @Transactional を追加する必要がありますか? クラス ProductDaoImpl はシングルトンでマルチスレッドで動作しますが、entityManager はスレッドセーフではないためです。

@Transactional により、Spring は Spring tx を現在のスレッドにバインドし、エンティティ マネージャーは、スレッド ローカルを介して現在のスレッドにバインドされている Spring tx にバインドされます。

Pro JPA 2 ブックの第 6 章には、この内容について適切な説明があります。少し内容が濃く、Java EE のコンテキストで説明されていますが、手順は春と同じです。

于 2013-05-15T05:01:08.697 に答える