以下博客链接对此行为进行了详细分析。http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/这是我的总结。
EntityManager
是一个java接口,它允许spring提供它自己的接口实现。spring 注入的实现使用动态代理来处理对实体管理器的调用。动态代理的行为方式如下。
如果没有@Transactional
注解,因为loadProductsByCategory
spring 在被调用时会创建一个EntityManager
em的实例em.createQuery
,spring 不会返回 JPA 创建的 Query 对象,但它会返回EntityManager
这个 spring 代理的 Spring Proxy 将所有调用转发给真正的实现Query
并等待直到getResult
or getSingleResult
orexecuteUpdate
被调用并立即关闭实体管理器。
因此,当没有@Transactional
Spring 时,将确保尽快关闭实体管理器,即在实体管理器上的每个方法调用之后或提取结果集之后。在上面的示例中,如果您注释掉 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.
}
}
回答你最后一个问题。
我是否需要将 @Transactional 添加到方法 loadProductsByCategory 才能将 EntityManager 绑定到线程?因为 ProductDaoImpl 类是单例的并且工作在多线程中,但是 entityManager 不是线程安全的。
@Transactional 导致 spring 将一个 spring tx 绑定到当前线程,然后实体管理器绑定到 spring tx,该 spring tx 通过线程本地绑定到当前线程。
Pro JPA 2 书在第 6 章中对这些内容进行了很好的解释,它有点密集,并且在 Java EE 的上下文中进行了解释,但是对于 spring 的步骤是相同的。