【问题标题】:What is the proper way to ensure EntityManager connections are closed?确保 EntityManager 连接关闭的正确方法是什么?
【发布时间】:2011-03-21 20:38:42
【问题描述】:

我们的 DAO 层有 19 种方法,每一种都是这种方法的一些变体:

public TicketProp saveTicketProp(TicketProp prop) {
    EntityManager em = this.emf.createEntityManager();
    try {
        em.getTransaction().begin();
        prop = (TicketProp) em.merge(prop);
        em.getTransaction().commit();
        return prop;
    } finally {
        em.close();
    }
}

含义:在每个方法中,我们处理自己的事务并在 finally 块中关闭它。我们正在测试一个 Jersey 应用程序,因此我们的 JUnit 测试扩展了 JerseyTest。每个测试方法都会实例化一个 Grizzly 容器,运行测试,然后关闭容器。 EntityManagerFactory 由 spring 注入。我们在 Hibernate 上使用 JPA。

我正在监视与我们的 MySQL 测试数据库的连接,它们总是很高。仅一项测试将 MySQL“Max_used_connections”变量运行到 38。为了好玩,我去注释掉所有 em.close() 调用,测试仍然使用 38 个连接。

我正在使用 Hibernate 的内置连接池(我知道,不是用于产品)。我仍然期待某种智能池。

我对 EntityManager 的处理是否错误?我还能如何关闭连接?

【问题讨论】:

  • em.close 只释放连接到连接池。 emf.close 将关闭所有连接。所以在您的应用程序中创建的 emf 可能太多,因此连接太多。

标签: mysql hibernate jpa jersey entitymanager


【解决方案1】:

您应该在测试结束时close EntityManagerFactory 。来自EntityManagerFactory#close()的javadoc:

void javax.persistence.EntityManagerFactory.close()

关闭工厂,释放它拥有的所有资源。关闭工厂实例后,对其调用的所有方法都将抛出IllegalStateException,但isOpen 除外,它将返回false。一旦EntityManagerFactory 被关闭,它的所有实体管理器都被认为处于关闭状态。

附带说明一下,您实际上应该在关闭 finally 子句中的 EM 之前回滚事务:

public TicketProp saveTicketProp(TicketProp prop) {
    EntityManager em = this.emf.createEntityManager();
    try {
        em.getTransaction().begin();
        prop = (TicketProp) em.merge(prop);
        em.getTransaction().commit();
        return prop;
    } finally {
        if (em.getTransaction().isActive()) {
            em.getTransaction().rollback();
        }

        if (em.isOpen()) {
            em.close();
        }
    }
}

【讨论】:

  • 事务不会在异常情况下自动回滚?我的 finally 块是否阻止了这种情况?
  • @gmoore:如果出现 JPA 异常,是的。但是其他人呢?我认为在 finally 块中调用回滚是一个好习惯。
  • 是的,em.close 只释放连接到连接池。 emf.close 将关闭所有连接。所以在您的应用程序中创建的 emf 可能太多,因此连接太多。
【解决方案2】:

为什么你认为EntityManager.close() 总是物理关闭底层连接?这取决于连接池(您可能需要对其进行配置并设置同时打开的最大连接数)。

【讨论】: