为什么JPA / Hibernate事务处于活动状态,即使我没有明确启动它

时间:2016-12-09 12:30:17

标签: java hibernate jpa transactions

我的问题是JPA / Hibernate为 entityManager.getTransaction()。isActive()的调用返回 true ,即使我没有显式启动事务(参见代码如下)。

这里的问题是我想从数据库中读取一些东西,并且在这种情况下SerializationException是可以的,因为这只是表明持久化对象不再适合实际代码,需要重新计算。下面的代码不是仅返回null,而是抛出以下异常:

Transaction rollback failed.
org.hibernate.TransactionException: Unable to rollback against JDBC Connection

这告诉我,我的代码中一定有一个我没有启动的事务。上面代码中的finally块是

final EntityManager entityManager = Persistence.createEntityManagerFactory("test").createEntityManager();

try {
    final TypedQuery<Test> query = entityManager.createQuery("SELECT t FROM test t", Test.class);

    return query.getResultList();

} catch (final PersistenceException e) {
    if (e.getCause() instanceof SerializationException) {
        LOG.debug("Implementation changed meanwhile. That's ok - we return null.");
        return null;
    }
    throw e;

} finally {
    EntityManagerCloser.closeEntityManager(entityManager);
}

EntityManagerCloser看起来像这样:

public final class EntityManagerCloser {
    private static final Logger LOG = LoggerFactory.getLogger(EntityManagerCloser.class);

    public static void closeEntityManager(EntityManager entityManager) {
        if (entityManager.getTransaction().isActive()) {
            try {
                entityManager.getTransaction().rollback();
            } catch (PersistenceException | IllegalStateException e) {
                LOG.error("Transaction rollback failed.", e);
            }
        }
        if (entityManager.isOpen()) {
            try {
                entityManager.close();
            } catch (IllegalStateException e) {
                LOG.error("Closing entity manager failed.", e);
            }
        }    
    }
}

Hibernate docs说“始终使用明确的事务边界,即使是只读操作”。所以我真的需要插入一个

entityManager.getTransaction().begin();
....
<do read here>
....
entityManager.getTransaction().commit();

围绕我在数据库上执行的每个读操作?

我可以在没有回滚事务块的情况下为只读操作实现另一个closeEntityManager方法,但我想了解为什么有一个事务。谢谢你的帮助!

2 个答案:

答案 0 :(得分:2)

The problem is that when you call entityManager.getTransaction(); a new transaction object will be created. So it is better to save the transaction reference to a variable as shown below.

Transaction txn = entityManager.getTransaction();

if (txn.isActive()) {
   try {
     txn.rollback();
     } catch (PersistenceException | IllegalStateException e) {
        LOG.error("Transaction rollback failed.", e);
      }
}

答案 1 :(得分:0)

Thanks to Jobin I quickly found the solution to my problem:

I think I need to call entityManager.isJoinedToTransaction() in my closeEntityManager method before calling entityManager.getTransaction().isActive().

This will prevent the EntityManagerCloser to start its own transaction which I can not rollback later because I did not explicitly call transaction.begin() on it.