【问题标题】:net.sf.hibernate.util.JDBCExceptionReporter - Connection org.postgresql.jdbc3g.Jdbc3gConnection is closednet.sf.hibernate.util.JDBCExceptionReporter - 连接 org.postgresql.jdbc3g.Jdbc3g连接已关闭
【发布时间】:2023-09-19 11:27:01
【问题描述】:

我在我的一个生产环境中遇到了这个问题。它经常发生,但并不总是在应用程序的同一部分发生,它非常随机。

部署在此环境中的应用程序与部署在不同机器上的其他应用程序非常相似,使用相同的技术和关闭设置,但错误仅发生在此环境中,因此很难找出问题所在。

我必须说这些错误最近开始出现,因为有几个更新更改:

应用程序从 Windows 迁移到 Linux

PostgresSQL 8.2 到 9.2

Tomcat 5 到 Tomcat 6

所有这些更改也已在其他未遇到任何问题的已部署应用程序上完成。

应用程序使用的技术是:

休眠 2.1.6

PostgreSQL 9.2

Tomcat 6.0.35

context.xml中的配置是:

<Resource name="jdbc/psa" auth="Container" type="javax.sql.DataSource"
        driverClassName="org.postgresql.Driver" url="jdbc:postgresql://localhost:5432/psa?compatible=7.4"
        username="xxx" password="xxx" maxActive="100" maxIdle="30"
        maxWait="10000" factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
        defaultAutoCommit="false" removeAbandoned="true"
        removeAbandonedTimeout="60" logAbandoned="true" validationQuery="select 1"
        testOnBorrow="true" testOnReturn="true" testWhileIdle="true"
        timeBetweenEvictionRunsMillis="300000"/>

请注意,驱逐活动只是试图解决问题,但没有成功。

接下来是一个典型问题的流程:

1.- 打开一个新的 Hibernate 会话

2.- 我在查询之前检查此会话是否已打开并连接

3.- 在日志上看到打开的会话有效后,产生了异常

--添加

其中一个错误的完整堆栈跟踪如下:

29 03 2013 10:00:00 INFO EasyApScheduler_Worker-8 com.psa.accounting.eureca.logic.EurecaNLAccountingLogic - -- Process EURECA_NL starts
    29 03 2013 10:00:00 INFO EasyApScheduler_Worker-2 com.psa.accounting.eureca.logic.EurecaSCAccountingLogic - -- Process EURECA_SC starts
    29 03 2013 10:00:00 DEBUG EasyApScheduler_Worker-2 com.xeridia.persistence.ServiceLocator - Session opened: net.sf.hibernate.impl.SessionImpl@22880d66. Opened: true, Connected: true
    29 03 2013 10:00:00 DEBUG EasyApScheduler_Worker-2 com.xeridia.persistence.ServiceLocator - [getConnection]|16|Thread[EasyApScheduler_Worker-2,4,main]|
    29 03 2013 10:00:00 DEBUG EasyApScheduler_Worker-2 com.xeridia.persistence.ServiceLocator - [getConnection]|16|Thread[EasyApScheduler_Worker-2,4,main]|com.easyap.invoice.persistence.InvoiceDAOHibernate.<init>(InvoiceDAOHibernate.java:364)
    29 03 2013 10:00:00 ERROR EasyApScheduler_Worker-8 net.sf.hibernate.util.JDBCExceptionReporter - Connection org.postgresql.jdbc3g.Jdbc3gConnection@1b0ca3f7 is closed.
    29 03 2013 10:00:00 DEBUG EasyApScheduler_Worker-2 com.xeridia.persistence.ServiceLocator - [getConnection]|16|Thread[EasyApScheduler_Worker-2,4,main]|com.psa.invoice.persistence.PSAInvoiceDAOHibernate.<init>(PSAInvoiceDAOHibernate.java:52)
    29 03 2013 10:00:00 ERROR EasyApScheduler_Worker-8 net.sf.hibernate.util.JDBCExceptionReporter - Connection org.postgresql.jdbc3g.Jdbc3gConnection@1b0ca3f7 is closed.
    29 03 2013 10:00:00 ERROR EasyApScheduler_Worker-8 net.sf.hibernate.util.JDBCExceptionReporter - Could not execute query
    java.sql.SQLException: Connection org.postgresql.jdbc3g.Jdbc3gConnection@1b0ca3f7 is closed.
        at org.apache.tomcat.dbcp.dbcp.DelegatingConnection.checkOpen(DelegatingConnection.java:398)
        at org.apache.tomcat.dbcp.dbcp.DelegatingConnection.prepareStatement(DelegatingConnection.java:279)
        at org.apache.tomcat.dbcp.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.prepareStatement(PoolingDataSource.java:313)
        at net.sf.hibernate.impl.BatcherImpl.getPreparedStatement(BatcherImpl.java:257)
        at net.sf.hibernate.impl.BatcherImpl.getPreparedStatement(BatcherImpl.java:232)
        at net.sf.hibernate.impl.BatcherImpl.prepareQueryStatement(BatcherImpl.java:65)
        at net.sf.hibernate.loader.Loader.prepareQueryStatement(Loader.java:779)
        at net.sf.hibernate.loader.Loader.doQuery(Loader.java:265)
        at net.sf.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:133)
        at net.sf.hibernate.loader.Loader.doList(Loader.java:1033)
        at net.sf.hibernate.loader.Loader.list(Loader.java:1024)
        at net.sf.hibernate.hql.QueryTranslator.list(QueryTranslator.java:854)
        at net.sf.hibernate.impl.SessionImpl.find(SessionImpl.java:1544)
        at net.sf.hibernate.impl.QueryImpl.list(QueryImpl.java:39)
        at com.xeridia.persistence.BaseDAOHibernate.findByQuery(BaseDAOHibernate.java:312)
        at com.xeridia.persistence.BaseDAOHibernate.findByQuery(BaseDAOHibernate.java:291)
        at com.psa.invoice.persistence.PSAInvoiceDAOHibernate.getAccountingInvoicesWithNoProblem(PSAInvoiceDAOHibernate.java:215)
        at com.psa.accounting.eureca.logic.EurecaNLAccountingLogic.getAccountingDataFromDB(EurecaNLAccountingLogic.java:469)
        at com.psa.accounting.eureca.logic.EurecaNLAccountingLogic.process(EurecaNLAccountingLogic.java:207)
        at com.psa.accounting.eureca.scheduler.CronEurecaNLAccounting.execute(CronEurecaNLAccounting.java:91)
        at org.quartz.core.JobRunShell.run(JobRunShell.java:203)
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520)

管理所有 Hibernate 会话的类是一个名为 ServiceLocator 的类。该类的getConnection()方法返回一个新的连接,其代码如下:

synchronized (session) {
                s = (Session) session.get();
                if ((s == null)||(!s.isOpen())) {
                    s = null;
                    s = getSessionFactory().openSession();
                    if (s != null) {
                        log.debug("Session opened: " + s + ". Opened: " + s.isOpen() +
                                ", Connected: " + s.isConnected());
                        printThreadDataForClosedConnections("[getConnection]");
                    }
                    session.set(s);
                }
                if (!s.isConnected()) {
                    s.reconnect();
                    log.debug("Session reconnected: " + s + ". Opened: " + s.isOpen() +
                            ", Connected: " + s.isConnected());
                    printThreadDataForClosedConnections("[getConnection]");
                }
            }

对象“会话”声明为:

public static final ThreadLocal session = new ThreadLocal();

为不同的线程获取不同的会话,所以线程之间关闭会话应该不存在问题。

您可以在上面的堆栈跟踪中看到,EasyApScheduler_Worker-2 因为if ((s == null)||(!s.isOpen())) { 获得了新连接,但 EasyApScheduler_Worker-8 没有,这意味着它的“会话”对象存在并且打开或无法获得连接这对应于错误29 03 2013 10:00:00 ERROR EasyApScheduler_Worker-8 net.sf.hibernate.util.JDBCExceptionReporter - Connection org.postgresql.jdbc3g.Jdbc3gConnection@1b0ca3f7 is closed.的第一个跟踪

我不知道问题是来自 Hibernate、Postgres 还是 Tomcat。我在 Google 上几乎没有发现任何关于此错误的信息,因此希望您能帮帮我。

请询问您需要的任何额外信息或设置以获得帮助。

提前致谢。

【问题讨论】:

  • 这看起来像一个多线程问题 - 您的第一个日志来自名为“EasyApAdminScheduler_Worker-2”的Thread,而您的异常来自名为“EasyApScheduler_Worker-8”的Thread。你在Threads 之间分享你的Session 吗?这实在不可取。无论如何,大概在第一个 Thread 关闭 Session 和第二个使用它的线程之间存在竞争风险。
  • 感谢您的回答。看看我原来的帖子,我添加了一些额外的信息。我写的日志跟踪是错误的,它来自其他线程。你是对的,有 2 个 Quartz 进程同时运行,但我还有很多其他情况,只有一个 Quartz 进程。如果需要,我可以从其他不同且更简单的错误中复制堆栈跟踪。
  • 我仍然认为这是一种种族危险。迁移/更新通常会使这些问题浮出水面。调试竞态危害相当困难,但您可以从附加调试器并在关键部分放置断点开始。为您的 ServiceLocator 编写一个集成测试,它会触发大量 Threads,这也可能有助于深入了解它。
  • 您可能想完全放弃重新发明*并使用与休眠捆绑在一起的(经过测试的)CurrentSessionContext 实现之一。 ManagedSessionContext 似乎很合适。
  • 感谢您的建议,我会尝试建议的选项。我会告诉你的。

标签: hibernate tomcat6 quartz-scheduler postgresql-9.2


【解决方案1】:

我已经解决了这个问题。

这是 Quartz 线程的问题。默认情况下,Quartz 管理一个线程池,这些线程池在不同作业的执行之间被重用。我的类ServiceLocator,负责打开和关闭连接,是基于ThreadLocal对象的。似乎我的一项计划作业没有正确关闭其连接,之后在与此作业相同的线程上执行的所有作业都导致了错误。

我不知道什么是有问题的作业,我最终所做的是将 Quartz 的默认连接池更改为自定义连接池,这样我为每个作业执行创建一个新线程,而不是为所有作业共享一定数量的线程。

这是我编写的 Quartz 自定义线程池,以防它对某人有用:

public class SimpleNoThreadPooling implements ThreadPool {

    public int getPoolSize() {
        return 0;
    }


    public Log getLog() {
        return LogFactory.getLog(SimpleNoThreadPooling.class);
    }

    public void initialize() throws SchedulerConfigException { }

    class WorkerThread extends Thread {

        private Runnable runnable = null;

        /**
         * <p>
         * Create a worker thread, start it, execute the runnable and terminate
         * the thread (one time execution).
         * </p>
         */
        WorkerThread(Runnable runnable) {
            this.runnable = runnable;
            start();
        }

        public void run() {
            if (runnable != null) {
                try {
                    runnable.run();
                } catch (Exception exceptionInRunnable) {
                    try {
                        getLog().error("Error while executing the Runnable: ",
                            exceptionInRunnable);
                    } catch(Exception e) {  
                        // ignore to help with a tomcat glitch
                    }
                }
            }
        }
    }

    public boolean runInThread(Runnable runnable) {
        new WorkerThread(runnable);
        return true;
    }

    public void shutdown(boolean waitForJobsToComplete) { }

}

【讨论】: