【问题标题】:How to re-deploy a hibernate-c3p0 project on tomcat 7 without getting strange c3p0 errors如何在tomcat 7上重新部署hibernate-c3p0项目而不会出现奇怪的c3p0错误
【发布时间】:2013-11-23 05:09:00
【问题描述】:

如果项目在 tomcat 7 上通过 netbeans 重新部署,则会出现类似

的错误
java.lang.IllegalStateException
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1600)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
    at com.mchange.v2.resourcepool.BasicResourcePool.checkIdleResources(BasicResourcePool.java:1481)
    at com.mchange.v2.resourcepool.BasicResourcePool.access$2000(BasicResourcePool.java:32)
    at com.mchange.v2.resourcepool.BasicResourcePool$CheckIdleResourcesTask.run(BasicResourcePool.java:1964)
    at java.util.TimerThread.mainLoop(Timer.java:512)
    at java.util.TimerThread.run(Timer.java:462)
Exception in thread "Timer-5" java.lang.NoClassDefFoundError: com/mchange/v2/resourcepool/BasicResourcePool$AsyncTestIdleResourceTask
    at com.mchange.v2.resourcepool.BasicResourcePool.checkIdleResources(BasicResourcePool.java:1481)
    at com.mchange.v2.resourcepool.BasicResourcePool.access$2000(BasicResourcePool.java:32)
    at com.mchange.v2.resourcepool.BasicResourcePool$CheckIdleResourcesTask.run(BasicResourcePool.java:1964)
    at java.util.TimerThread.mainLoop(Timer.java:512)
    at java.util.TimerThread.run(Timer.java:462)
Caused by: java.lang.ClassNotFoundException: com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
    ... 5 more

今天我们尝试在 tomcat 7 上重新部署项目时遇到另一个奇怪的错误

[5:07:02 PM] Nitin - Webscraper/Tester,Java/PHP developer: java.lang.NoClassDefFoundError: com/mchange/v2/lang/VersionUtils
 com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:104)
 com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:65)
 com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:62)
 com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:531)
 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)
 org.hibernate.connection.C3P0ConnectionProvider.getConnection(C3P0ConnectionProvider.java:78)
 org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446)
 org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167)
 org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:161)
 org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1700)
 org.hibernate.loader.Loader.doQuery(Loader.java:801)
 org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
 org.hibernate.loader.Loader.doList(Loader.java:2542)
 org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2276)
 org.hibernate.loader.Loader.list(Loader.java:2271)

很长一段时间以来,我们一直在收到这些奇怪的错误。当我们尝试调试时,我们发现这些类已经存在。

我能想到的是悬挂 c3p0 连接池线程,这些线程要么在重新部署时未正确销毁,要么可能正在执行一些活动连接或类似的东西。

对于如何重新部署这样一个使用 hibernate 和 c3p0 的项目,是否有任何最佳实践?为了正确关闭 c3p0 线程,我必须在 contextDestroyed 上编写一些代码吗?

【问题讨论】:

    标签: java hibernate tomcat tomcat7 c3p0


    【解决方案1】:

    我也遇到了同样的问题,我可以在我的 tomcat 控制台中看到以下警告

    2014 年 7 月 30 日下午 3:20:16 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads 警告:Web 应用程序 [/rmlcrm] 似乎已经启动了一个名为 [C3P0PooledConnectionPoolManager[identityToken->1hge50p9311d8syo1hfjimz|19ddf1db]-HelperThread-#0] 的线程,但未能停止它。这很可能造成内存泄漏。线程的堆栈跟踪: java.lang.Object.wait(本机方法) com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:635)

    我阅读了很多内容以找到解决方案,并发现了该帖子 Hibernate :OutOfMemoryError: PermGen space

    Nicholas Hemley 帖子中的一条评论建议添加自定义 ServletContextListener 并在监听器的 contextDestroyed() 方法中显式关闭 C3P0 连接,该方法将在取消部署应用程序时执行。

    我们没有完全使用代码,因为我们不想与 C3P0 硬耦合。但是我们意识到我们并没有在应用程序的任何地方关闭hibernate sessionFactory。 我们在 ServletContextListener 的 contextDestroyed() 中添加了关闭休眠会话工厂的代码。 现在我们没有错误,也没有在 tomcat 控制台中收到警告。

    您可能还想阅读Hibernate : closing the session factory does not close the c3p0 connection pool

    【讨论】:

    • 使用 contextDestroyed() 关闭 sessionFactory 为我工作。
    【解决方案2】:

    一些想法:

    1) 如果您已将 hibernate 应用程序的生命周期设置为映射到您的网络应用程序的生命周期(如果 hibernate 和 c3p0 库位于您的网络应用程序的 lib 目录中,则绝对正确,即使没有也可能正确),您绝对需要确保在应用程序回收之前销毁 c3p0 池,这通常意味着 contextDestroyed 方法。在 hibernate-speak 中,是 SessionFactory 包装了连接池;当您的应用程序在热重新部署时关闭时,请确保您的应用程序的 SessionFactory 已关闭()。应该有一个对称性:在contextInitialized 或在第一次请求时懒惰地,你的 SessionFactory 应该被初始化。它应该在应用程序关闭时被销毁。

    2) 最新(仍为预发布)版本的 c3p0 有一些设置旨在减少 c3p0 线程和源自过期 web 应用类加载器的对象之间的污染可能性,尤其是当 c3p0 由非 web 应用加载时特定的类加载器(例如,如果 c3p0 库位于 $CATALINA_HOME/lib 而不是 webapp 库目录中)。如果您愿意升级到预发布版 [现在最新的是 c3p0-0.9.5-pre5],请尝试以下新配置设置:

     c3p0.privilegeSpawnedThreads=true
     c3p0.contextClassLoaderSource=library
    

    希望这会有所帮助!

    【讨论】:

    • 我正在使用hibernate-c3p0 dependency,其中包括它自己的依赖版本的 c3p0。在这种情况下我还能升级吗?我想我会在类路径中有多个 c3p0 jar,这绝对不是一个好主意。
    • 不,绝对要避免使用多个版本,尤其是在 Tomcat 中。乘法类加载器已经够令人困惑了。你会想要强制传递依赖解析到较新的 c3p0,参见例如stackoverflow.com/questions/3937195/…(细节取决于你的构建方式,但总有办法。)
    • 另请注意,hibernate-c3p0 使用较旧的 c3p0:c3p0 工件,而较新的 c3p0 构建使用 com.mchange:c3p0(不同的 groupIds)。因此,如果您要走这条路线,请确保从 hibernate-c3p0 中排除 c3p0:c3p0 依赖项,并声明对较新的 com.mchange:c3p0 的单独依赖项。
    猜你喜欢
    • 2017-09-03
    • 1970-01-01
    • 2016-06-06
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 2019-08-26
    相关资源
    最近更新 更多