问题
I was reading EJB Transaction boundary and Transaction boundary
Lets concentrate on RequiresNew Attribute
.
Here is the modified diagram from the link
So let say method-B
is annotated with RequiredNew attribute
.
so according to theory when method-A
calls method-B
a new transaction will be start and the already started transaction will be suspended, and when method-B
returns the new transaction will be committed.
Now consider that in section S1
we create a jpa entity using entitymanager.persist()
and now we pass this entity to method-B
which set the name
field of the entity.
Now when we return from method-B
how can it commit
the transaction as in db, the entity is not being committed by the suspended transaction started by method-A
?
PS: Db running in read-committed isolation level.
回答1:
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
callsBean2MethodB
and passes in ’entity’ as a parameter.- New Transaction – TransactionB starts, TransactionA suspended.
Bean2-MethodB
modifies fieldentity.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 toBean1-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.
来源:https://stackoverflow.com/questions/30636273/ejb-jpa-transaction-boundaries