Reputation: 10864
Following code gets executed whenever I want to persist any entity. Things seems to be working fine but I fail to understand how it works !
EntityManager em = getEntityManager();
EntityTransaction userTransaction = em.getTransaction();
userTransaction.begin();
em.persist( ent );
userTransaction.commit();
The EntityManager above is a single instance shared by whole application. After starting the transaction; I just say em.persist(entity).. How does hibernate know it belongs to which transaction !
Suppose there are 10 concurrent users on my application and all 10 threads executing above code. So 10 independent transactions are getting created and committed. But all 10 different entities I am not associating them with their respective transactions; so how is JPA able to work it out !
Based on answers; we have below; are we saying that we should have an EntityManager instance per thread ? Will that not be a kill on the server ! Should we be pooling these instances ? Will it not be equal to again implementing sort of Connection Pooling ?
Upvotes: 12
Views: 30029
Reputation: 42084
It works because you are lucky enough. Lucky enough means that commit and begin are called in right order - accidentally.
You do use single instance of entity manager from multiple threads. That is wrong thing to do, because it is not guaranteed to be thread safe. Access to resource level transaction via EntityTransaction is bound to entity manager instance, not to the thread.
So result is that you are sharing same EntityTransaction and using it luckily serially for multiple transactions. Using it serially to strart and end multiple transaction is fine, but using it from many threads is not.
In hibernate (4.1.4) reference is stored to tx instance field in AbstractEntityManageImpl class, but that is just implementation detail.
Upvotes: 6
Reputation: 1276
It's using ThreadLocal variables for the Transaction.
See also the documentation for UserTransaction:
begin()
Create a new transaction and associate it with the current thread.
You should not share the EntityManager though since it is not guaranteed to be thread-safe.
However if you are injecting it in an EJB, you don't have to worry about thread-safety: http://www.adam-bien.com/roller/abien/entry/is_in_an_ejb_injected
If you are using Spring to inject it, you'll get a thread-safe proxy: http://static.springsource.org/spring/docs/3.1.1.RELEASE/spring-framework-reference/html/orm.html#orm-jpa-straight
Although EntityManagerFactory instances are thread-safe, EntityManager instances are not. The injected JPA EntityManager behaves like an EntityManager fetched from an application server's JNDI environment, as defined by the JPA specification. It delegates all calls to the current transactional EntityManager, if any; otherwise, it falls back to a newly created EntityManager per operation, in effect making its usage thread-safe.
Upvotes: 7
Reputation: 4137
I recommend you understand how JTA works regardless of Hibernate - it's quite important for your to understand
In addition, read about container-managed transactions and bean managed transactions.
If you're working in a container-managed transaction, you can specify for the bean you're injecting the EntityManager to what is the transaction scope -
for example - if the scope is REQUIRED, it means that if another bean calls this bean, and not in context of transaction, a new transaction will be open. If a transaction already exists, then you will use the same transaction. It is important to understand this, as transactions are an expensive resource in your system.
A transaction object is associated with ThreadLocal, however, another thread may resume a suspended transaction, depends on the implementation of your TransactionManager (I am talking about JBossTransactionManager)
Upvotes: 0
Reputation: 691715
The transaction is associated with the current thread somehow, using a ThreadLocal variable.
Upvotes: 2