1

从不包含任何脏对象的实体管理器提交事务时会发生什么?会不会没有向数据库发送 COMMIT 命令?

我时不时有一些测试用例在没有合理原因的情况下失败。经过一番调查,我现在有了一个我想在这里证实的理论。

我有一个小型夹具框架来为每个测试准备数据库上的数据。夹具使用这种方法使用 JPA (Hibernate) 将对象存储到数据库:

  public <R> R doInTransaction(final Function<EntityManager, R> whatToDo) {
    final EntityManager em = emf.createEntityManager();
    final R result;
    try {
      try {
        em.getTransaction().begin();
        result = whatToDo.apply(em);
        em.getTransaction().commit();
      } finally {
        if (em.getTransaction().isActive()) {
          em.getTransaction().rollback();
        }
      }
    } finally {
      em.close();
    }
    return result;
  }

因此,fixture 调用此方法传递 whatToDo 函数,其中对象被持久化,并且该方法围绕传递的函数包装事务。我失败的测试用例使用的夹具依赖于使用存储过程并直接通过 JDBC 存储对象的遗留代码,即em.persist()我没有使用 ,而是在传递的函数中使用以下内容来调用存储过程:

em.unwrap(Session.class).doWork(connection -> {
  // stored procedures are called here directly over JDBC
});

因此,我的理论是,在这种情况下,JPA 不会立即提交,因为 EntityManager 没有管理 JPA 脏对象。因此,实际提交仅在稍后发生,即在我的测试断言并且测试失败之后。可以吗?

当从 EntityManager 中“解包”连接时,Hibernate 的事务行为是什么?

我现在在em.flush()之前添加了一个em.getTransaction().commit(),它似乎有帮助,但我仍然不能 100% 确信这可以解决问题。有人可以确认吗?

4

1 回答 1

1

无论打开连接,行为都是相同的。如果你不使用JTA,另一种选择是JDBC提供的底层事务,即本地事务。(或者您可以实现自己的托管事务提供程序)

当您解开连接并直接处理 JDBC 时,您仍然会获得最初由该会话/实体管理器获得的相同连接。所以效果是一样的。

于 2017-02-21T18:58:45.457 回答