我读了 Spring 文档,它说:
The @PersistenceContext annotation has an optional attribute type, which defaults to PersistenceContextType.TRANSACTION. This default is what you need to receive a shared EntityManager proxy.
- 这是否意味着我必须让 EntityManager 在事务中工作?
- 对于非事务性方法(读取查询)(例如下面代码中的 loadProductsByCategory)如何工作?
- “共享”是什么意思?如何使用EntityManager与其他人共享?
我是否需要将 @Transactional 添加到方法 loadProductsByCategory 才能将 EntityManager 绑定(bind)到线程?因为类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); } }
最佳答案
以下博客链接对此行为进行了详分割析。 http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/这是我的总结。
EntityManager
是一个java接口(interface),它允许spring提供它自己的接口(interface)实现。 spring 注入(inject)的实现使用动态代理来处理对实体管理器的调用。动态代理的行为方式如下。
如果没有@Transactional
注释,如loadProductsByCategory
中,spring将在em.createQuery时创建
被调用时,spring不会返回JPA创建的Query对象,而是返回EntityManager
em的实例EntityManager
的Spring Proxy,这个spring代理将所有调用转发给Query
的真正实现> 它会等待,直到调用 getResult
或 getSingleResult
或 executeUpdate
并立即关闭实体管理器。
因此,当没有@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.
}
}
回答你最后一个问题。
Do I need to add @Transactional to the method loadProductsByCategory in order to bind the EntityManager to the thread? Because the class ProductDaoImpl is singleton and works in multi-thread, but entityManager is not thread-safe.
@Transactional 导致 spring 将 spring tx 绑定(bind)到当前线程,然后将实体管理器绑定(bind)到通过当前线程本地线程绑定(bind)的 spring tx。
Pro JPA 2 书在第 6 章对这些内容进行了很好的解释,它有点密集,并且是在 Java EE 的上下文中进行解释的,但步骤对于 spring 是相同的。关于spring - 如何使用事务范围的持久化上下文进行非事务性读取查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10577110/