javaee7-samples
javaee7-samples copied to clipboard
Add @RequestScoped EntityManager Example
A very common pattern is to use an EntityManager which is @RequestScoped with CDI. This is often referred to as the entitymanager-per-request pattern.
Currently I am unable to make this work in coordination with @Transactional without going SynchronizationType.UNSYNCHRONIZED and manually calling an entityManager.joinTransaction()
public class PersistenceResources {
@PersistenceContext(type = PersistenceContextType.EXTENDED, synchronization = SynchronizationType.SYNCHRONIZED)
private EntityManager em;
@Produces
@RequestScoped
private EntityManager requestScopedEntityManager() {
return em;
}
}
Unfortunately all the entities appear to get detached after the first transaction commits. Is this pattern possible with EE 7 without having to manually join to the transaction?
This was done in DeltaSpike with an @PersistenceUnit EntityManagerFactory but that utilized its own Transactions module to handle the synchronization of the EntityManager to the Transaction. See - https://deltaspike.apache.org/jpa.html
@arun-gupta I have an example here https://github.com/codylerum/eebox/tree/requestscoped_em that shows the issue. (use the requestscoped_em branch)
There is an link to both the working and the broken from the context root /eebox (tested on wildfly 8.1.0.Final)
What appears to be happening is that the enitymanager only joins the first transaction it is envolved in even though there may be multiple during the request.
Can you deploy this and see if you are seeing the same? From what I understand it shouldn't be behaving like this. If you see the same maybe you can check with the jpa+tx experts on your side to see why and if it is an issue or working as designed.
thanks for detailed explanation, I've asked some folks to look at it.
@arun-gupta Thanks. Let me know if there is anything else I can do to move this along.
@arun-gupta Arun FWIW this does appear to have the same behavior with Glassfish as it does with wildfly.
PersistenceContextType.EXTENDED only works with EJB stateful session beans. What happens if you use transaction scoped persistence context instead?
@PersistenceContext private EntityManager em;
We have pretty good TRACE logging in WildFly that helps illustrate what is happening. Enable trace logging (http://|https//docs.jboss.org/author/display/WFLY9/JPA+Reference+Guide#JPAReferenceGuide-Troubleshooting) for org.jboss.jca, org.hibernate, org.jboss.as.jpa and com.arjuna to get more information about what is occurring exactly (from tx start to tx end).
@scottmarlow The EM detaches everything after the first tx completes.
If I use
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
@Produces
@RequestScoped
public EntityManager entityManager() {
log.infof("Producing Entity Manager");
return entityManagerFactory
.createEntityManager(SynchronizationType.SYNCHRONIZED);
}
The EntityManager stays alive but it will not automatically join another transaction. It will join a first and I can manually join it to others with an EntityManager#joinTranscation() I don't know if this is a bug or just something that was thought of with regards to TX and EntityManager in EE 7
From JPA 2.1 specification: "7.7 Application-managed Persistence Contexts
When an application-managed entity manager is used, the application interacts directly with the persistence provider's entity manager factory to manage the entity manager lifecycle and to obtain and destroy persistence contexts.
All such actions application-managed persistence contexts are extended in scope, and can span multiple transactions.
The EntityManagerFactory.createEntityManager method and the EntityManager close and isOpen methods are used to manage the lifecycle of an application-managed entity manager and its associated persistence context.
The extended persistence context exists from the point at which the entity manager has been created using EntityManagerFactory.createEntityManager until the entity manager is closed by means of EntityManager.close.
An extended persistence context obtained from the application-managed entity manager is a standalone persistence context—it is not propagated with the transaction.
When a JTA application-managed entity manager is used, an application-managed persistence context may be specified to be of type SynchronizationType.UNSYNCHRONIZED. A persistence context of type SynchronizationType.UNSYNCHRONIZED is not enlisted in any JTA transaction unless explicitly joined to that transaction by the application. A persistence context of type Synchro- nizationType.UNSYNCHRONIZED is enlisted in a JTA transaction and registered for subsequent transaction notifications against that transaction by the invocation of the EntityManager joinTransaction method. The persistence context remains joined to the transaction until the transaction commits or rolls back. After the transaction commits or rolls back, the persistence context will not be joined to any subsequent transaction unless the joinTransaction method is invoked in the scope of that subsequent transaction.
When a JTA application-managed entity manager is used, if the entity manager is created outside the scope of the current JTA transaction, it is the responsibility of the application to join the entity manager to the transaction (if desired) by calling EntityManager.joinTransaction. If the entity manager is created outside the scope of a JTA transaction, it is not joined to the transaction unless EntityManager.joinTransaction is called.
The EntityManager.close method closes an entity manager to release its persistence context and other resources. After calling close, the application must not invoke any further methods on the EntityManager instance except for getTransaction and isOpen, or the IllegalStateException will be thrown. If the close method is invoked when a transaction is active, the persistence context remains managed until the transaction completes.
The EntityManager.isOpen method indicates whether the entity manager is open. The isOpen method returns true until the entity manager has been closed. "
I included the enter section for completeness but really just want to point out the seventh paragraph which I will repeat again here so everyone can avoid reading the entire above text if they like :)
" When a JTA application-managed entity manager is used, if the entity manager is created outside the scope of the current JTA transaction, it is the responsibility of the application to join the entity manager to the transaction (if desired) by calling EntityManager.joinTransaction. If the entity manager is created outside the scope of a JTA transaction, it is not joined to the transaction unless EntityManager.joinTransaction is called. "
Sounds like you are seeing that the application-managed entity manager, is not auto-joining any subsequent JTA transaction, which is correct as per the above paragraph.
@scottmarlow OK. That sounds like this is correct behavior then according to spec and it would just require manual joining. Thanks!
I'd love to see an example still. So what was the conclusion of this topic? Is the producer code still valid? Where does joinTransaction() come in to play?