【问题标题】:Cleaning up after changing the isolation level in JPA / EclipsLink EntityManager在 JPA / EclipseLink EntityManager 中更改隔离级别后进行清理
【发布时间】:2021-05-12 11:37:10
【问题描述】:

我正在使用自定义事务隔离级别在 JPA (EclipseLink) 中执行事务,我使用此代码在 JPA EntityManager 的底层连接上设置了该级别:

// 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);

如果我在提交事务后尝试将隔离级别重置为旧值,则底层连接为nullentityManager.unwrap(Connection.class) 返回 null)。我很担心,如果我不重新设置隔离级别,那么隔离级别不好的连接会泄漏回池中。

更改隔离级别后清理的正确方法是什么?我应该在致电commit() 之前这样做吗?

【问题讨论】:

    标签: java jpa transactions eclipselink


    【解决方案1】:

    java.sql.Connection 在对 entityManager.getTransaction().commit(); 的调用中返回到池中,因此之后重置隔离级别是不可能的,并且 EclipseLink 通过返回 null 连接来阻止。

    维护对Connection 的引用以规避此问题可能会泄漏更改设置的连接,因此我无法接受您的回答 RomanC

    我最终创建了两个EntityManagerFactory 实例。一种是创建默认的EntityManagers,另一种是使用SessionCustomizer创建带有我想要的事务级别的连接的EntityManagers

    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);
    }
    

    请看这里Set Isolation level in eclipselink

    然后我使用EntityManagerFactory,它提供了我需要的任何连接类型。警告:事务不能跨越多个EntityManagers 中的EntityManagerFactories

    【讨论】:

      【解决方案2】:

      试试下面的代码

      Connection conn = entityManager.unwrap(Connection.class);
      
      conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
      
      [...Queries...]
      
      // commit transaction
      entityManager.getTransaction().commit();
      
      conn.setTransactionIsolation(isolationLevelOld);
      

      【讨论】:

      • 我查看了 Eclipselink 源代码:java.sql.ConnectionDatabaseAccessor 封装,该DatabaseAccessor 返回到entityManager.getTransaction().commit(); 上的池中。我不确定池的生命周期以及它是否是 EntityManager 本地的,但如果不是,这意味着在调用 commit 后重置事务隔离级别为时已晚,因为连接可能已在其间的其他地方重用两个电话。
      • 当然不是,但是如果你从池中获取连接应该设置默认隔离,当你开始一个新事务时会使用这个隔离,在某些情况下你可以改变隔离当前交易的水平,但我认为这不是你的情况。当你提交一个事务你不能再使用它,甚至改变它的隔离级别,但是如果你关闭一个连接它不会关闭一个到数据库的连接而是返回一个连接对象到池中,你也可以在关闭之前测试它确保连接有效。
      猜你喜欢
      • 2015-10-16
      • 1970-01-01
      • 2013-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      • 2012-03-31
      • 2017-04-29
      相关资源
      最近更新 更多