Hibernate,C3P0,postgres,Tomcat连接在事务中空闲

时间:2013-10-08 12:28:42

标签: hibernate c3p0

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

的persistence.xml:

  

< persistence-unit name =“tv-europe-core”   交易类型= “RESOURCE_LOCAL” >         <提供商GT; org.hibernate.ejb.HibernatePersistence< /提供商GT;

     

---班级---

     

<性状> < property name =“hibernate.dialect”   value =“org.hibernate.dialect.PostgreSQLDialect”/>
  <属性   name =“hibernate.connection.password”value =“--- password ---”/>
  < property name =“hibernate.connection.url”value =“--- database ---”   />
  < property name =“hibernate.connection.username”   value =“--- username ---”/>

     

< property name =“hibernate.connection.driver_class”   value =“org.postgresql.Driver”/>
  <属性   name =“hibernate.connection.release_mode”value =“after_statement”/>

  < property name =“hibernate.connection.autocommit”value =“false”   />

     

< 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”   值= “5”/>
  <属性   name =“hibernate.c3p0.testConnectionOnCheckin”value =“true”/>

  < /性状>
  < /持久性单元>

保存对象:

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)  

感谢您的帮助! :)

3 个答案:

答案 0 :(得分:4)

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

  1. 交易类型=&#34; RESOURCE_LOCAL&#34;意味着您不在JTA环境中。
  2. property name =&#34; hibernate.connection.release_mode&#34;值=&#34; AFTER_STATEMENT&#34;结合auto-commit = false非常罕见。
  3. &#34; AFTER_STATEMENT&#34;仅当您的连接提供程序支持主动发布时才能使用(并且能够为同一事务中的每个语句返回相同的连接)。

    hibernate可能会忽略AFTER_STATEMENT(因为hibernate会检测到此版本模式与您的设置不兼容),而且会使用AFTER_TRANSACTION ......但只是为了确保你不会这样做。 ; t错误地使用它

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

    这将在非JTA环境中设置AFTER_TRANSACTION释放模式。

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

    修改

    BTW,重建sessionFactory似乎很奇怪。 SessionFactory通常是单例,sessionFactory的主要目的是非常有效地构建新的Session。一般来说,没有理由重新创建它(由于sessionFactory仅依赖于静态数据,因此耗时且无用)。

    我可以看到重新创建sessionFactory的唯一原因是你的应用程序在运行时更改数据模型-i.e.创建新的表或列 - (只有在应用程序修改映射文件或字节代码的同时添加新的注释,字段和类时,新的会话工厂才会看到这些更改)。我假设你没有这样做。

    编辑2

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

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


    编辑3 (作为对您上次评论的回复)

    您正面临着一个非常常见的建筑设计问题,让我们说有两种方法可以解决它。好的和(非常)坏的。

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

    这个想法是重新打开一个entityManager并在生成视图时重新附加你的实体,这样你就不会得到lazy-init异常。从短期来看,这种方法会让您误以为您的应用运行良好。一旦它将在生产中与您的数据库中的许多并发用户和/或越来越多的记录:您有巨大的风险,有真正的大的性能问题(无论是内存使用和/或响应时间)。

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

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

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

    好的:使用分层架构。

    视图图层无法触及实体管理器。该层接收要从控制器层显示的数据,所有数据都在那里:无需在此处获取延迟集合。

    控制器层有两个角色:

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

    另外,控制器层必须向视图层提供完整的对象图。完整对象图表示如果视图需要显示该集合中的数据,则视图层将不会收到未初始化的延迟集合。

    DAO图层

    只需执行查询即可获取数据(即编写JPA / HQL / SQL查询的位置)。除了使用控制器层提供的entityManager之外,该层不对entityManager执行任何特殊操作。

    DAO层必须提供各种查询,以便在有或没有它的懒惰集合的情况下获取此实体,以满足控制器层的所有需求。

    分层体系结构方法的主要优点是,您很快就会在开发过程中看到视图的要求,并且您可以在需要时调整和优化查询。 (即你需要逐个修复所有lazy-init异常,但它会让你对你的观点的要求有一个很好的看法)

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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