HibernateUtil不返回与c3p0池的连接

时间:2015-03-03 18:11:51

标签: java multithreading hibernate connection-pooling c3p0

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

为了调试问题,我遵循了this question中的建议,因为我的代码也在awaitAvailable中挂起。因此,我使用checkoutTimeout来阻止客户端无限期地等待unreturnedConnectionTimeoutdebugUnreturnedConnectionStackTraces一起获取不返回连接的代码部分的堆栈跟踪。令人惊讶的是,它是所有相同的代码(都在等待连接而不是返回它)并且它在我的HibernateUtil类中初始化会话工厂。

以下是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类加载和相应的静态块实际上是线程安全的

这是我的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需要多个连接来进行设置?为什么还不够?

2 个答案:

答案 0 :(得分:1)

  

是java类加载和相应的静态块实际上是线程安全的吗?

是。来自Java Virtual Machine Specification

  

因为Java虚拟机是多线程的,所以初始化一个   类或接口需要仔细同步,因为其他一些   线程可能正在尝试初始化相同的类或接口   同时。还有可能初始化一个   可以递归地请求类或接口作为其一部分   初始化该类或接口。 执行   Java虚拟机负责处理同步   并使用以下过程进行递归初始化。

请注意,一个类可能由多个类加载器加载,在这种情况下,您会看到静态初始化程序被调用两次。

  

但据我了解堆栈跟踪,它实际上停在了   课程加载

排序。这就是堆栈跟踪中的clinitJVM spec 2.9)。这实际上是在初始化期间发生的,而不是加载。

<property name="hibernate.c3p0.timeout">300</property>

你确定这个值不是以毫秒为单位吗? 300非常短,可以很容易地解释你所看到的debugUnreturnedConnectionStackTraces

答案 1 :(得分:1)

maxPoolSize - 您通过hibernate.c3p0.max_size设置的 - 应该大于一。如果一个操作需要超过maxPoolSize个连接并且无法完成util,那么非常小的值会使您的应用程序冻结。请参阅主要问题下方的评论主题。