I read the Spring doc and it says:
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.
- 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); } }
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
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.if there is no
@Transactional
annotation as inloadProductsByCategory
spring will create an instance of theEntityManager
em whenem.createQuery
is called, spring will not return the Query object created by JPA but it will return Spring Proxy ofEntityManager
this spring proxy forwards all calls to the real implementation ofQuery
and it waits until thegetResult
orgetSingleResult
orexecuteUpdate
are called and it immediately closes the Entity Manager.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 closedWhen 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.
To answer your last last question.
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 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.