java - HibernateUtil 不返回与 c3p0 池的连接

标签 java multithreading hibernate connection-pooling c3p0

在我们的 GWT Web 应用程序中使用 Hibernate 出现数据库连接超时的一些问题后,我们选择使用 c3p0 作为连接池提供程序。现在我遇到了一个不同的问题:应用程序似乎没有将连接返回到池。 相反,它会在第一次数据库访问时停止。

为了调试该问题,我遵循了 this question 中的建议。 ,因为我的代码也卡在 waitAvailable 中。因此,我使用 checkoutTimeout 来防止客户端无限期等待,并使用 unreturnedConnectionTimeoutdebugUnreturnedConnectionStackTraces 来获取未返回的代码部分的堆栈跟踪。返回连接。令人惊讶的是,它们都是相同的代码(都等待连接和不返回连接的代码),并且都在我的 HibernateUtil 类中,该类初始化 session 工厂。

这是由 debugUnreturnedConnectionStackTraces 保存的堆栈跟踪:

java.lang.Exception: DEBUG STACK TRACE: Overdue resource check-out stack trace.
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:555)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:755)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:682)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
at org.hibernate.c3p0.internal.C3P0ConnectionProvider.getConnection(C3P0ConnectionProvider.java:90)
at org.jadira.usertype.spi.shared.AbstractUserTypeHibernateIntegrator.use42Api(AbstractUserTypeHibernateIntegrator.java:80)
at org.jadira.usertype.spi.shared.AbstractUserTypeHibernateIntegrator.integrate(AbstractUserTypeHibernateIntegrator.java:61)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:312)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859)
at my.package.domain.hibernate.HibernateUtil.<clinit>(HibernateUtil.java:17)
[... snip ...] // user code calling HibernateUtil.getSessionFactory()

awaitAvailable() 超时的类似堆栈跟踪:

com.mchange.v2.resourcepool.TimeoutException: A client timed out while waiting to acquire a resource from com.mchange.v2.resourcepool.BasicResourcePool@ae6163 -- timeout at awaitAvailable()
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1416)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:606)
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:526)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:755)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:682)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
at org.hibernate.c3p0.internal.C3P0ConnectionProvider.getConnection(C3P0ConnectionProvider.java:90)
at org.jadira.usertype.spi.shared.AbstractUserTypeHibernateIntegrator.use42Api(AbstractUserTypeHibernateIntegrator.java:80)
at org.jadira.usertype.spi.shared.AbstractUserTypeHibernateIntegrator.integrate(AbstractUserTypeHibernateIntegrator.java:61)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:312)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859)
at my.package.domain.hibernate.HibernateUtil.<clinit>(HibernateUtil.java:17)
[... snip ...] // same client code calling HibernateUtil.getSessionFactory()

对服务器的一个请求就足以导致此问题,因此不存在多个请求。但调用 getSessionFactory 的代码如下所示:

public class MyClass
{
    private SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    // [...] snip
}

因此,MyClass 的对象可能会在应用程序的不同部分并行实例化,这可能会导致多次调用 getSessionFactory()。

但据我了解堆栈跟踪,它实际上在类加载中停止(而不是在对 getSessionFactory() 的调用中),我希望这是线程安全的。但是java类加载和相应的静态 block 实际上是线程安全的吗?

这是我的 HibernateUtil 代码:

public class HibernateUtil
{
    private static SessionFactory sessionFactory;

    static
    {
        Configuration configuration = new Configuration().configure();
        StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
        sessionFactory = configuration.buildSessionFactory(builder.build());
    }

    public static SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }
}

它应该这样工作吗?有什么需要改进的地方吗?

为了完整起见,这是我的 hibernate.cfg.xml

<hibernate-configuration>
<session-factory>
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">
    <property name="show_sql">true</property>
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

    <!-- Connection pool configuration -->
    <property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
    <property name="hibernate.c3p0.min_size">1</property>
    <property name="hibernate.c3p0.max_size">1</property>
    <property name="hibernate.c3p0.timeout">300</property>
    <property name="hibernate.c3p0.max_statements">50</property>
    <property name="hibernate.c3p0.idle_test_period">90</property>
    <property name="hibernate.c3p0.checkoutTimeout">10000</property><!-- milliseconds -->
    <property name="hibernate.c3p0.debugUnreturnedConnectionStackTraces">true</property><!-- do not use in production -->
    <property name="hibernate.c3p0.unreturnedConnectionTimeout">60</property>

    <!-- Configure automatic session management: https://developer.jboss.org/wiki/Sessionsandtransactions#jive_content_id_Transaction_demarcation_with_plain_JDBC -->
    <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
    <property name="hibernate.current_session_context_class">thread</property>

    <!-- Configure automatic mapping for Joda Time classes like DateTime and 
        Instant -->
    <property name="jadira.usertype.autoRegisterUserTypes">true</property>
    <property name="jadira.usertype.javaZone">UTC</property>
    <property name="jadira.usertype.databaseZone">UTC</property>

    <property name="hibernate.hbm2ddl.auto">update</property>
    <!-- lots of mapped classes -->

</session-factory>
</hibernate-configuration>

编辑:按照@Steve Waldmann的建议,我增加了 maxPoolSize,结果如下:

* 2 Connections lead to the same problem
* 4 Connections lead to the same problem
* __8 Connections worked__

然后我再次尝试使用较小的 maxPoolSize,现在4 个连接也能正常工作。但3次连接仍然失败。我认为问题是由于我所做的架构更新造成的。显然 hibernate 需要更多连接来进行架构升级,但至少在我的情况下至少需要 3 个连接来进行初始设置。

所以我想剩下的唯一问题是,为什么 Hibernate 需要多个连接来进行设置?为什么一个还不够?

最佳答案

maxPoolSize — 您通过 hibernate.c3p0.max_size 设置— 应大于 1。如果一个操作需要超过maxPoolSize,那么非常小的值会使您的应用程序容易卡住。连接并无法完成使用它拥有它们。请参阅主要问题下方的评论线程。

关于java - HibernateUtil 不返回与 c3p0 池的连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28838931/

相关文章:

java - 如何在 LDAP 中使用 DN 和密码在 Java 中进行绑定(bind)?

Java 线程以延迟结束

java - 为什么它没有通过线程?

java - 如何在 Hibernate 中一对多映射 bean 属性

java - Hibernate 没有更新数据库记录

spring - Hibernate 不创建表 postgreSQL

java - 需要帮助理解此方法

java - 检索 List 中 Map 的值

java - 无法从字符串中获取值

java - 调用 postAtTime 时处理程序会自动跳过其他可运行对象/消息