I’m performing a transaction in JPA (EclipseLink) using a custom transaction isolation level, which I set on the underlying connection of the JPA EntityManager
using this code:
// begin transaction entityManager.getTransaction().begin(); // store the old isolation level int isolationLevelOld = entityManager.unwrap(Connection.class).getTransactionIsolation(); // set the desired isolation level for this transaction entityManager.unwrap(Connection.class).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); [...Queries...] // commit transaction entityManager.getTransaction().commit(); // reset isolation level to the old value (throws NullPointerException) entityManager.unwrap(Connection.class).setTransactionIsolation(isolationLevelOld);
If I try to reset the isolation level to the old value after having committed the transaction, the underlying connection is null
(entityManager.unwrap(Connection.class)
returns null). I’m worried, if I just don’t reset the isolation level, a connection with a bad isolation level gets leaked back to the pool.
What is the correct way of cleaning up after changing the isolation level? Should I maybe do it before calling commit()
?
Advertisement
Answer
The java.sql.Connection
gets returned to the pool in the call to entityManager.getTransaction().commit();
So resetting the isolation level afterwards is not possible and prevented by EclipseLink by returning a null
connection.
Maintaining a reference to the Connection
to circumvent this will likely leak a connection with altered settings, so I cannot accept your answer RomanC
I ended up creating two instances of EntityManagerFactory
. One that creates default EntityManagers
and one that creates EntityManagers
with Connections with my desired transaction level using a SessionCustomizer
:
public static class SessionCustomizer implements org.eclipse.persistence.config.SessionCustomizer { @Override public void customize(Session session) throws Exception { DatabaseLogin databaseLogin = (DatabaseLogin) session.getDatasourceLogin(); databaseLogin.setTransactionIsolation(DatabaseLogin.TRANSACTION_SERIALIZABLE); } } private void init() { entityManagerFactoryRegular = Persistence.createEntityManagerFactory("MyPersitenceRegular"); Map<String, String> props = new HashMap<>(); props.put(PersistenceUnitProperties.SESSION_CUSTOMIZER, SessionCustomizer.class.getName()); entityManagerFactoryTransactionSerializable = Persistence.createEntityManagerFactory("MyPersitenceTransactionSerializable", props); }
See here Set Isolation level in eclipselink
I then use the EntityManagerFactory
that provides whichever connection type I need. Caveat: Transactions cannot span EntityManagers
from multiple EntityManagerFactories