在我们的GWT webapp中使用Hibernate的数据库连接超时后,我们选择使用c3p0作为连接池提供程序。现在我有一个不同的问题:该应用似乎没有返回到池的连接。 相反,它会在第一次数据库访问时停止。
为了调试问题,我遵循了this question中的建议,因为我的代码也在awaitAvailable中挂起。因此,我使用checkoutTimeout
来阻止客户端无限期地等待unreturnedConnectionTimeout
和debugUnreturnedConnectionStackTraces
一起获取不返回连接的代码部分的堆栈跟踪。令人惊讶的是,它是所有相同的代码(都在等待连接而不是返回它)并且它在我的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需要多个连接来进行设置?为什么还不够?
答案 0 :(得分:1)
是java类加载和相应的静态块实际上是线程安全的吗?
是。来自Java Virtual Machine Specification:
因为Java虚拟机是多线程的,所以初始化一个 类或接口需要仔细同步,因为其他一些 线程可能正在尝试初始化相同的类或接口 同时。还有可能初始化一个 可以递归地请求类或接口作为其一部分 初始化该类或接口。 执行 Java虚拟机负责处理同步 并使用以下过程进行递归初始化。
请注意,一个类可能由多个类加载器加载,在这种情况下,您会看到静态初始化程序被调用两次。
但据我了解堆栈跟踪,它实际上停在了 课程加载
排序。这就是堆栈跟踪中的clinit
(JVM spec 2.9)。这实际上是在初始化期间发生的,而不是加载。
<property name="hibernate.c3p0.timeout">300</property>
你确定这个值不是以毫秒为单位吗? 300非常短,可以很容易地解释你所看到的debugUnreturnedConnectionStackTraces
。
答案 1 :(得分:1)
maxPoolSize
- 您通过hibernate.c3p0.max_size
设置的 - 应该大于一。如果一个操作需要超过maxPoolSize
个连接并且无法完成util,那么非常小的值会使您的应用程序冻结。请参阅主要问题下方的评论主题。