问题描述
我阅读了Spring文档,上面写着:
I read the Spring doc and it says:
- 这是否意味着我必须使EntityManager在事务中工作?
- 它如何用于非事务方法(读取查询),例如下面的代码中的loadProductsByCategory?
- 共享"是什么意思?如何与他人共享EntityManager?
-
是否需要将@Transactional添加到方法loadProductsByCategory以便将EntityManager绑定到线程?因为类ProductDaoImpl是单例且可在多线程中工作,所以EntityManager不是线程安全的.
- Does this mean I have to make EntityManager work in a transaction?
- How does it work for non-transactional method (reading query), such as the loadProductsByCategory in the below code?
- What does the "shared" mean? How can it use EntityManager sharing with others?
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.
@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/这是我的总结.
There is a detailed analysis of this behavior at the following blog link. http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/ Here is my summary of it.
EntityManager
是一个Java接口,允许spring提供它自己的接口实现. Spring注入的实现使用动态代理来处理对实体管理器的调用.动态代理的行为方式如下.
EntityManager
is a java interface which allows spring to provide it's own implementation of the interface. The implementation injected by spring use a dynamic proxy to handle the calls to the entity manager. The dynamic proxy behaves the following way.
如果没有@Transactional
注释(如在loadProductsByCategory
中一样),则在调用em.createQuery
时spring将创建EntityManager
em的实例,spring将不会返回JPA创建的Query对象,但会返回Spring Proxy EntityManager
的此春季代理将所有调用转发到Query
的实际实现,并等待直到调用getResult
或getSingleResult
或executeUpdate
并立即关闭实体管理器.
if there is no @Transactional
annotation as in loadProductsByCategory
spring will create an instance of the EntityManager
em when em.createQuery
is called, spring will not return the Query object created by JPA but it will return Spring Proxy of EntityManager
this spring proxy forwards all calls to the real implementation of Query
and it waits until the getResult
or getSingleResult
or executeUpdate
are called and it immediately closes the Entity Manager.
因此,当没有@Transactional
时,Spring将确保尽快关闭实体管理器,即在实体管理器上的每个方法调用之后或提取结果集之后.在上面的示例中,如果注释掉query.getResultList(),最终将泄漏未关闭的实体管理器实例
So when there is no @Transactional
Spring will make sure that the entity manager is closed as soon as possible, i.e. after each method call on the entity manager or after a result set is extracted. In your above example if you comment out the query.getResultList() you will end up leaking an entity manager instance that does not get closed
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实例并将其与当前的跨国公司相关联,如果事务性方法调用了另一个方法,该方法又调用了另一个方法,并且所有这些方法都使用了一个实体管理器,那么该实体管理器就是在所有这些通话中共享.这是一个例子.
When there is @Transactional attribute the spring transaction manager will create a transactional context before the transactional method is called. When transactional method calls any method on the entity manager Spring will create a brand new EntityManager instance and associate it with current transnational, if the transactional method, calls another method, which calls another and all those methods use an entity manager then the entity manager is shared across all those calls. here is an example.
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.
}
}
回答您的最后一个问题.
To answer your last last question.
@Transactional使spring将spring tx绑定到当前线程,然后将实体管理器绑定到spring tx,后者通过当前线程本地的线程绑定.
The @Transactional causes spring to bind a spring tx to the current thread, and then the entity manager is bound to the spring tx which is bound via thread local to the current thread.
Pro JPA 2本书在第6章对此内容进行了不错的解释,虽然有点密集,但在Java EE的上下文中进行了解释,但步骤与Spring相同.
Pro JPA 2 book has a decent explanation of this stuff in chapter 6, it is bit dense and it is explained in the context of Java EE but the steps are the same for spring.
这篇关于如何将事务范围的持久性上下文用于非事务性读取查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!