What happens in this scenario is governed by:
- The JPA Persistence Context and its relationship to JTA transactions.
- The behaviour of Java’s “pass-by-reference” like parameter passing on local interfaces.
(See note on pass-by-reference at the end of this answer)
Creating an Entity Manager with the @PersistenceContext
annotation leads to the creation of a Transaction-Scoped entity manager and associated persistence context as defined by your persistence.xml
file. The context will keep track of the entities of the types specified in your persistence.xml
. An entity becomes managed in that context after it is persisted, found (em.find()
) or merged. The context will be associated with the currently running JTA transaction. When this transaction ends, the changes present in the persistence context can be flushed and committed - or rolled-back if the transaction itself rolls back.
In the example scenario assume Bean2’s local interface is used. When Bean2-MethodB
is called a new transaction is started since the method is annotated with @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
. The transaction of the calling method is suspended. No persistence context is associated with the newly created transaction. However, the entity passed in will refer to the same instance of the entity processed by Bean1-MethodA
, and this instance is a managed entity in the persistence context of Bean1’s Entity Manager. So, even though the transaction associated to this persistence context is suspended, there are no restrictions on modifying that entity from within another transaction.
So, the nominal sequence of events is;
Bean1-MethodA
is called, TransactionA starts.
- Transaction-Scoped Entity Manager created, persistence context associated with TransactionA.
Bean1-MethodA
calls Bean2MethodB
and passes in ’entity’ as a parameter.
- New Transaction – TransactionB starts, TransactionA suspended.
Bean2-MethodB
modifies field entity.name
.
- Bean2-MethodB finishes and TransactionB commits.
- TransactionB JTA persistent resources commit - entity is in persistence context associated with TransactionA and so is not committed.
- Bean1-MethodA resumes as does its associated TransactionA.
- Entity changes made in
Bean2-MethodB
are visible to Bean1-MethodA
and the persistence context
Bean1-MethodA
finishes, TransactionA commits and persistence context changes are flushed/committed to DB
- --> DB contains field change made in
Bean2-MethodB
What happens when Bean2-MehodB’s Transaction rolls-back?
It is worth noting that if entity field changes are made in Bean2-MethodB
, and Bean2-MethodB’s
transaction rolls-back, changes to class fields are not reverted. Any JTA resources will be rolled-back, but the entity field changes would still be reflected in the DB If Bean1-MehodA
completes successfully, leading to potential inconsistencies. It maybe that real world problems might force such a solution, but probably better to modify entities in the transaction that can roll those changes back.
The above scenarios were tested on eclipse-mars/WildFly8.2/HibernateJPA/Derby
Working with Remote EJB calls
Here, the entity parameter is serialised resulting in a copy of the entity in Bean2-MethodB
. This copy is not the same object as used in Bean1-MethodA
, it is a detached entity and is not shared with Bean1-MethodA
. (This is sometimes known as pass-by-value, see note at the end of this answer). In order for changes to be reflected in Bean1-MethodA’s
persistence context the entity would need to be returned to Bean1-MethodA
and then merged into the persistence context using the Entity Manager. This merging would be required regardless of whether or not the remote ejb call was made within a transaction.
Note on “Pass-by-reference” and “Pass-by-value”.
All parameters in java are pass by value by definition. For a good - extended - discussion on Stack overflow see Is Java "pass-by-reference" or "pass-by-value"?. What’s important here is that for local interfaces, Java passes a copy of the reference – a pointer - to the shared instance – and this is what people often understand as “pass-by-reference”. Remote interfaces create a copy of the entity instance at the remote end so the calling method has no visibility of any changes to this copy. This is sometimes known as pass-by-value.