javajpatransactionseclipselinkentitymanager

Cleaning up after changing the isolation level in JPA / EclipsLink EntityManager


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()?


Solution

  • 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