Am I in the same transaction? Am I using the same PersistenceContext?

Recently, I’ve bumped into few posts on StackOverflow where people tend to compare container managed EntityManager instances (so the one injected by the container) by invoking EntityManager#toString() method. I’ve felt that it’s fundamentally wrong to compare EntityManager instances without knowing how they’re managed by the JPA provider or the Server Application. And what if this behaviour differs between Application Server vendors?

The JPA provider provides an implementation of EntityManager – that’s obvious. More interesting is that the Application Server is scanning all @PersistenceContext fields and wrapping these EntityManagers into its own class – a proxy – which delegates requests to the JPA provider’s EntityManager. Therefore, if you’re comparing results of toString() method of such EntityManagers, you can’t say about PersistenceContexts equality but rather about Server Application EntityManager equality.

Needless to say, an Application Server could use one proxy as an access point to different EntityManagers. Without knowing the internals, you’re not able to say how it will work and decide if your results are meaningful.

One of the ways to cope with this problem might be to unwrap the container-provided EntityManager proxy to get to the JPA-provided one. You can do that either using:

In this way you’re one step closer to the victory.

On this point, it’s worth of noticing, that by the JPA specification, given Persistence Context, when used in transactional environment, is bound to exactly one transaction and is accessible from all components which participates in this transaction. So, maybe we could just compare if the transaction is the same and it would be enough to confirm that the PersistenceContexts are the same? You could check transactions for equality using TransactionSynchronizationRegistry#getTransactionKey().

Both situations: unwrapping of EntityManager and testing transactions for equality, have been presented in following code.

Moreover, this code shows one more, very important thing which might be hard to analyse. In order to invoke EJB call, you need to use it’s business interface. If you don’t do that, you’ll end invoking a local method call which doesn’t have any EJB nature – the container is not able to intercept such method invocation. Therefore, for local call, the @TransactionAttribute will have no meaning.

Take a look at the following code. I’m using EclipseLink and Glassfish 3.1.1. The client invokes method1().

Lets examine the exemplary results of the invocation:

INFO: [method1] Server proxy for EntityManager EM: com.sun.enterprise.container.common.impl.EntityManagerWrapper@173d62d

INFO: [method1] EclipseLink EntityManager: org.eclipse.persistence.internal.jpa.EntityManagerImpl@1f243df

INFO: [method1] Tx key: JavaEETransactionImpl: txId=45 nonXAResource=23 jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@462718, com.sun.enterprise.resource.pool.PoolManagerImpl$SynchronizationListener@1b0d75f]

INFO: [method2] Server proxy for EntityManager EM: com.sun.enterprise.container.common.impl.EntityManagerWrapper@10ba812

INFO: [method2] EclipseLink EntityManager: org.eclipse.persistence.internal.jpa.EntityManagerImpl@475614

INFO: [method2] Tx key: JavaEETransactionImpl: txId=46 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@1644679]

INFO: [method2] Is Tx1 the same as Tx2? false

Now let’s modify the code a bit and remove or comment the @TransactionAttribute fragment, so method2() will have a default Tx attribute – REQUIRED. In this case, it will reuse the transaction of method1(). Let’s look at the results:

INFO: [method1] Server proxy for EntityManager EM: com.sun.enterprise.container.common.impl.EntityManagerWrapper@178fd24

INFO: [method1] EclipseLink EntityManager: org.eclipse.persistence.internal.jpa.EntityManagerImpl@33517f

INFO: [method1] Tx key: JavaEETransactionImpl: txId=48 nonXAResource=95 jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@12b86b2, com.sun.enterprise.resource.pool.PoolManagerImpl$SynchronizationListener@985158]

INFO: [method2] Server proxy for EntityManager EM: com.sun.enterprise.container.common.impl.EntityManagerWrapper@469bc

INFO: [method2] EclipseLink EntityManager: org.eclipse.persistence.internal.jpa.EntityManagerImpl@33517f

INFO: [method2] Tx key: JavaEETransactionImpl: txId=48 nonXAResource=95 jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@12b86b2, com.sun.enterprise.resource.pool.PoolManagerImpl$SynchronizationListener@985158]

INFO: [method2] Is Tx1 the same as Tx2? true

So, as you can see – only after unwrapping the container-provided EntityManager we were able to see that we, in fact, use the same EntityManager.

At the end, let’s see get back to the original code but change the invocation of method2() from method1() to be a local call, so we’ll be using myMethod2(txKey) instead of ctx.getBusinessObject(MyEJB.class).myMethod2(txKey). Exemplary results might be as follows:

INFO: [method1] Server proxy for EntityManager EM: com.sun.enterprise.container.common.impl.EntityManagerWrapper@110d926

INFO: [method1] EclipseLink EntityManager: org.eclipse.persistence.internal.jpa.EntityManagerImpl@12062da

INFO: [method1] Tx key: JavaEETransactionImpl: txId=50 nonXAResource=95 jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@139defe, com.sun.enterprise.resource.pool.PoolManagerImpl$SynchronizationListener@5f299d]

INFO: [method2] Server proxy for EntityManager EM: com.sun.enterprise.container.common.impl.EntityManagerWrapper@110d926

INFO: [method2] EclipseLink EntityManager: org.eclipse.persistence.internal.jpa.EntityManagerImpl@12062da

INFO: [method2] Tx key: JavaEETransactionImpl: txId=50 nonXAResource=95 jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@139defe, com.sun.enterprise.resource.pool.PoolManagerImpl$SynchronizationListener@5f299d]

INFO: [method2] Is Tx1 the same as Tx2? true

I hope this sum up what are possible situations you might bump into when using transactional, container-managed EntityManager.

The code snippet which I presented here can be found on my Gist.

References: