3

我们有一个使用 Hibernate 连接到 postgre 数据库的应用程序。我们使用 C3P0 作为连接池。

持久性.xml:

<persistence-unit name="tv-europe-core" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider>

---课程---

<properties> <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.connection.password" value="---password---" />
<属性名称="hibernate.connection.url" value="---数据库---" />
<property name="hibernate.connection.username" value="---用户名---" />

<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.connection.release_mode" value="after_statement" />
<property name="hibernate.connection.autocommit " 值="假" />

<property name="hibernate.c3p0.minPoolSize" value="5"/>
<property name="hibernate.c3p0.maxPoolSize" value="60"/>
<property name="hibernate.c3p0.maxIdleTime" value=" 10"/>
<property name="hibernate.c3p0.idleConnectionTestPeriod" value="5"/>
<property name="hibernate.c3p0.testConnectionOnCheckin" value="true"/>
</properties>
</persistence-unit>

保存对象:

public Entity saveOrUpdate(Entity entity, User u) {  
EntityTransaction tx = EntityManagerHelper.getEntityManager().getTransaction();  
try {  
  if(!tx.isActive())  
      tx.begin();  
          Entity result = null;  
      if (getID(entity) == null) {  
      EntityManagerHelper.getEntityManager().persist(entity);  
  } else {  
      result = EntityManagerHelper.getEntityManager().merge(entity);  
  }  
  tx.commit();    
  return result;  
  } catch (RuntimeException re) {  
      re.printStackTrace();  
      tx.rollback();  
      throw re;  
  }  
}  

加载对象:

@SuppressWarnings("unchecked")
    public List<Entity> findByProperty(String propertyName, final Object value,
            final int... rowStartIdxAndCount) {

        try {
            final String queryString = "select model from " + clazz.getName()
                    + " model where model." + propertyName + "=     :propertyValue";
            Query query = EntityManagerHelper.getEntityManager().createQuery(
                    queryString);
            query.setParameter("propertyValue", value);
            if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0)     {
                int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
                if (rowStartIdx > 0) {
                    query.setFirstResult(rowStartIdx);
                }

                if (rowStartIdxAndCount.length > 1) {
                    int rowCount = Math.max(0, rowStartIdxAndCount[1]);
                    if (rowCount > 0) {
                        query.setMaxResults(rowCount);
                    }
                }
            }
            final List<Entity> result = query.getResultList();
            return result;
        } catch (RuntimeException re) {
            re.printStackTrace();
            throw re;
        }
    }  

创建 EntityManagerFactory 并获取 EntityManager:

private static EntityManagerFactory emf;
private static final ThreadLocal<EntityManager> threadLocal = new ThreadLocal<EntityManager>();  

public static EntityManager getEntityManager() throws HibernateException {
    EntityManager session = (EntityManager) threadLocal.get();

    if (session == null || !session.isOpen()) {
        session = (emf != null) ? emf.createEntityManager()
                : null;
        threadLocal.set(session);
    }

    return session;
}

问题是,一个数据库连接一次又一次地停留在“事务中的空闲”状态,之后这个连接就永远不会返回。几天后,应用程序停止响应,因为连接数超过了池的最大大小。
启用 hibernate hibernate.connection.autocommit 后,这些连接不会变成“事务中的空闲”,但它们仍然以某种方式被阻塞,并且产生的问题是相同的。

我们是否做错了什么(一些缺少配置等)?

我注意到,当我只使用急切加载时,没有问题。但由于性能,我必须使用延迟加载。EntityManagerFactory 是否应该显式关闭?我希望不会,因为我们需要应用程序运行很长时间,而且我怀疑当有人使用持久对象时我不能重置它。

在日志中我看到以下内容,但我不知道它是否与我们的问题有关:

java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE
    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:491)
    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:191)
    at     com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.dest    royResource(C3P0PooledConnectionPool.java:470)
    at     com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.ja    va:964)
    at     com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunn    er.java:547)  

谢谢你的帮助!:)

4

3 回答 3

4

您的配置对我来说看起来不正确:

  1. transaction-type="RESOURCE_LOCAL" 表示您不在 JTA 环境中。
  2. property name="hibernate.connection.release_mode" value="after_statement" 结合 auto-commit = false 是非常少见的。

仅当您的连接提供程序支持积极释放(并且能够为同一事务中的每个语句返回相同的连接)时,才能使用“after_statement”。

hibernate 可能会忽略 AFTER_STATEMENT (因为 hibernate 检测到此发布模式与您的设置不兼容)并且将使用 AFTER_TRANSACTION 来代替......但只是为了确保您不会错误地使用它

 <property name="hibernate.connection.release_mode" value="auto" />

这将在非 JTA 环境中设置 AFTER_TRANSACTION 发布模式。

我不确定这是否会解决您的问题(因为您可能已经在 after_transaction 模式下运行)。(如果不能解决,请发表评论,需要进行更深入的调查)。

编辑

顺便说一句,重建 sessionFactory 似乎很奇怪。SessionFactory 通常是一个单例,sessionFactory 的主要目的是非常有效地构建新的 Session。一般来说,没有理由重新创建它(因为 sessionFactory 只依赖于静态数据,所以既费时又无用)。

我可以看到重新创建 sessionFactory 的唯一原因是如果您的应用程序在运行时更改数据模型 - 即创建新表或列 - (并且只有在同时您应用程序修改您的映射文件或字节码 - 添加新的注释、字段和类 - )。我假设你没有这样做。

编辑 2

正如我在之前的编辑中所说:避免重建 sessionFactory。将此方法设为私有,以确保不会多次调用它。如果您的代码正在重新构建 sessionFactory 这可能是您的问题的原因,因为新的 SessionFactory 可能会消耗一些连接 - 由于相关的 C3PO 设置)。

另一点:你说禁用延迟加载时:没问题了。所以问题也可能是由为延迟加载创建的会话而没有正确关闭造成的。尝试调试延迟加载操作以查看会话来自何处,以及它是否已关闭。


编辑 3(作为对您最后评论的回复)

您正面临一个非常常见的架构设计问题,假设有两种方法可以解决它。好的和(非常)坏的。

(非常)糟糕的一个:使用视图模式中的打开会话。

这个想法是在生成视图时重新打开一个 entityManager 并重新附加您的实体,这样您就不会遇到惰性初始化异常。在短期内,这种方法会给你一种错误的感觉,即你的应用程序运行良好。一旦它投入生产,有许多并发用户和/或数据库中的记录越来越多:您将面临真正的大性能问题(关于内存使用和/或响应时间)的巨大风险。

(这些潜在问题的根本原因是您在开发小数据库期间不会注意到这个或那个视图正在获取具有 10 个对象的延迟初始化集合......但在生产中,您的小集合将变得巨大有 10000 个对象!!!)

这些问题将很难解决,因为: - 它们将出现在多个视图中 - 它们将难以进行单元/负载测试(因为它们在视图层中)。

在我看来,这种方法只能用于不会有巨大负载或大量数据的小型非关键应用程序。

好一个:使用分层架构。

视图层不接触实体管理器。该层从控制器层接收要显示的数据,所有数据都在那里:这里不需要获取惰性集合。

控制器层有两个角色:

  • 实现你的业务逻辑
  • 管理 entityManager 生命周期(和事务边界)并提供可供 DAO 层使用的 entityManager。

此外,控制器层必须向视图层提供完整的对象图。完整的对象图意味着如果视图需要显示来自该集合的数据,视图层将不会收到未初始化的惰性集合。

DAO 层

只需执行查询以获取数据(这是您编写 JPA/HQL/SQL 查询的地方)。除了使用控制器层提供的 entityManager 之外,该层对 entityManager 没有任何特殊处理。

DAO 层必须提供广泛的查询来获取这个实体,无论是否有惰性集合,以便满足控制器层的所有需求。

分层架构方法的主要优点是您可以在开发过程中很快看到您的视图的需求,并且您将能够在需要时调整和优化您的查询。(即您需要一一修复所有惰性初始化异常,但它会让您对视图的要求有一个很好的了解)

于 2013-10-08T14:14:59.333 回答
0

将 hibernate.connection.release_mode 属性值更改为 after_transaction。我认为这应该可以解决您的问题

于 2015-03-17T13:04:22.297 回答
0

我还修改了我的“选择方法” - 我将它包含在事务中(就像在我的保存方法中一样)并且行为发生了变化:就像启用 hibernate.autocommit 时,连接不会以“事务中的空闲”状态结束,但他们仍然被封锁,而且他们的数量还在增加。它们不会被 hibernate.c3p0.maxIdleTime 设置破坏(其他连接是)。当我在每次选择后关闭 Entitymanager 时,我稍后会得到 LazyInitializationException。这些连接是否可能以某种方式保留给延迟加载的对象?

于 2013-10-10T09:33:31.853 回答