この動作の詳細な分析は、次のブログ リンクにあります。http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/これが私の要約です。
EntityManager
Spring がインターフェースの独自の実装を提供できるようにする Java インターフェースです。Spring によって注入された実装は、動的プロキシを使用してエンティティ マネージャーへの呼び出しを処理します。動的プロキシは次のように動作します。
@Transactional
Spring のようにアノテーションがない場合、呼び出されたときに emloadProductsByCategory
のインスタンスが作成されます。Spring は JPA によって作成された Query オブジェクトを返しませんが、Spring を返します。この Spring プロキシのプロキシはすべての呼び出しを実際の実装に転送し、待機します。またはまたはが呼び出されるまで、すぐにエンティティ マネージャーを閉じます。EntityManager
em.createQuery
EntityManager
Query
getResult
getSingleResult
executeUpdate
そのため、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 のコンテキストで説明されていますが、手順は春と同じです。