首页 文章

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

提问于
浏览
2

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

persistence.xml中:

<persistence-unit name =“tv-europe-core”transaction-type =“RESOURCE_LOCAL”> <provider> org.hibernate.ejb.HibernatePersistence </ provider> --- classes --- <properties> <property name = “hibernate.dialect”value =“org.hibernate.dialect.PostgreSQLDialect”/> <property 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“/> <property 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”value =“5”/> <property name =“hibernate.c3p0.testConnectionOnCheckin”value =“true”/ > </ properties> </ persistence-unit>

保存对象:

public Entity saveOrUpdate(实体实体,用户u){
EntityTransaction tx = EntityManagerHelper.getEntityManager() . getTransaction();
尝试{
如果(!tx.isActive())
tx.begin();
实体结果= null;
if(getID(entity)== null){
EntityManagerHelper.getEntityManager()持续(实体) .
} else {
result = EntityManagerHelper.getEntityManager() . merge(entity);
}
tx.commit();
返回结果;
} catch(RuntimeException re){
re.printStackTrace();
tx.rollback();
抛出;
}
}

装货对象:

@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;
}

问题是,数据库连接一次又一次保持在"idle in transaction"状态,之后永远不会返回此连接 . 几天后,应用程序停止响应,因为连接数超过了池最大大小 .
当启用hibernate hibernate.connection.autocommit时,这些连接不会变成"idle in transaction",但它们仍会以某种方式被阻止,并且产生的问题是相同的 .

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

我注意到,当我只使用急切加载时,没有问题 . 但由于性能原因,我不得不使用延迟加载 . 是否应该明确关闭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 回答

  • 4

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

    • transaction-type = "RESOURCE_LOCAL"表示您不在JTA环境中 .

    • 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模式下运行) . (如果不解决,请发表评论,并需要进行更深入的调查) .

    EDIT

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

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

    EDIT 2

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

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


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

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

    The (very) bad one : using the open session in view pattern.

    我们的想法是在生成视图时重新打开entityManager并重新附加实体,这样就不会出现延迟初始化异常 . 在短期内,这种方法会让您错误地认为您的应用运行良好 . 一旦它将在 生产环境 中与您的数据库中的许多并发用户和/或越来越多的记录:您有很大的风险,有真正的大性能问题(无论是内存使用和/或响应时间) .

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

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

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

    The good one : using a layered architecture.

    The view layer 请勿触摸实体管理器 . 该层接收要从控制器层显示的数据,所有数据都在那里:无需在此处获取延迟集合 .

    The controller layer 有两个角色:

    • 实现您的业务逻辑

    • 管理entityManager生命周期(和事务边界)并提供可由DAO层使用的entityManager .

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

    The DAO layer

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

    DAO层必须提供各种查询才能获取此实体,无论是否具有惰性集合,都可以满足控制器层的所有需求 .

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

  • 0

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

  • 0

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

相关问题