Java 使用JPA、Hibernate&;帮助我避免连接超时;MySQL

Java 使用JPA、Hibernate&;帮助我避免连接超时;MySQL,java,hibernate,jpa,jakarta-ee,connection-pooling,Java,Hibernate,Jpa,Jakarta Ee,Connection Pooling,我正在使用JPA(Hibernate作为提供者)、Glassfish和MySQL。在开发过程中,一切都很好,但当我将应用程序部署到测试服务器并让它在一夜之间运行(大部分是空闲的)时,我通常会在早上听到这样的消息: [#|2011-03-09T15:06:00.229+0000|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=23;_ThreadName=Th

我正在使用JPA(Hibernate作为提供者)、Glassfish和MySQL。在开发过程中,一切都很好,但当我将应用程序部署到测试服务器并让它在一夜之间运行(大部分是空闲的)时,我通常会在早上听到这样的消息:

[#|2011-03-09T15:06:00.229+0000|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=23;_ThreadName=Thread-1;|ERROR [htt\
p-thread-pool-8080-(1)] (JDBCTransaction.java:91) - JDBC begin failed
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 41,936,868 milliseconds ago.  The last packet \
sent successfully to the server was 41,936,868 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expirin\
g and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connec\
tion property 'autoReconnect=true' to avoid this problem.
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:532)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
        at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1118)
        at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3321)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1940)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2113)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2562)
        at com.mysql.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl.java:4956)
        at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:87)
        at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1473)
        at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:60)
我尝试在我的
persistence.xml
中使用以下内容,但没有任何帮助:

        <property name="hibernate.c3p0.min_size" value="5"/>
        <property name="hibernate.c3p0.max_size" value="20"/>
        <property name="hibernate.c3p0.idleTestPeriod" value="30"/>
        <property name="hibernate.c3p0.timeout" value="0"/>
        <property name="hibernate.c3p0.max_statements" value="0"/>

这就是C3p0的配置;我完全可能遗漏了告诉hibernate“嘿,使用c3p0”的部分

我将尝试错误消息中的建议:将
autoReconnect=true
添加到我的JDBC URL中,但这真的开始让人感觉像是货物崇拜的发展。我希望能就解决这一问题的适当方式提供一些指导。调试很困难,因为测试周期实际上是“通宵运行,早上看看会发生什么”

我可能应该提到我是如何在我的应用程序中使用连接的。我有一个截取所有请求的定制。它创建EntityManager,将其存储在ThreadLocal中,并由catch/finally块中的过滤器关闭。我的所有实体都从
ThreadLocal
获取对
EntityManager
的引用

完全有可能是我的过滤器出了故障,但由于这似乎只发生在空闲时间之后,我怀疑是其他原因出了问题。当我有机会喘口气时,我确实打算转到Seam/Weld,但现在我依靠这个过滤器

编辑:这里是TL;灾难恢复解决方案:

  • 如果可以,请使用容器的连接池(谢谢,@partenon)
  • 确保您的连接池使用连接验证(谢谢@matt b)
在我的例子中,我必须进入Resources/JDBC/connectionpools下的Glassfish控制台的Advanced选项卡,然后启用连接验证:

这确实是关键的一步。您可能还希望将
最多验证一次
设置为合理的值,例如100秒。如果您使用的是C3P0或类似产品,请确保配置了
idle\u test\u period
preferredTestQuery

不管你最终做了什么,重要的是测试你的改变,看看它们是否达到了预期的效果。为了加快MySQL中的超时,您可以通过编辑
my.cnf
wait\u timeout
临时设置为30秒之类的较低值。这对调试这个问题是一个巨大的帮助,因为它允许我在几秒钟而不是几小时内测试更改

您应该考虑在应用程序使用之前对连接有效性进行期满和/或测试,增加客户端超时的服务器配置值,或者使用连接器/J连接属性“AutoReNeNeCT= true”来避免此问题。


这只是瞎猜,但是您是否已经在JDBC驱动程序中设置了
autoReconnect=true
属性?或者考虑禁用客户端连接超时的服务器端设置。

< P>我认为真正的问题是:为什么使用外部连接池机制而不是使用GalasFISH自己的池?您的应用程序服务器更适合为您的应用程序提供此类服务。“外部”连接池机制更适合于独立应用程序,而不是容器应用程序

根据C3P0文档,我认为设置连接测试周期的属性是
idle\u test\u period
,而不是
idleTestPeriod
。因此,您应该使用:

<property name="hibernate.c3p0.idle_test_period" value="30"/>


相反。

我遇到了同样的问题,花了很多时间才找到解决办法

我使用Hibernate4.0.1和MySQL5.1(没有spring框架),我面临着这个问题。首先确保正确配置了c3p0 JAR,这是必不可少的

我在hibernate.cfg.xml中使用了这些属性

<property name="hibernate.c3p0.validate">true</property>
<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.preferredTestQuery">SELECT 1;</property>
<property name="hibernate.c3p0.testConnectionOnCheckout">true</property>
<property name="hibernate.c3p0.idle_test_period">10</property>
<property name="hibernate.c3p0.acquireRetryAttempts">5</property>
<property name="hibernate.c3p0.acquireRetryDelay">200</property>
<property name="hibernate.c3p0.timeout">40</property>
<property name="hibernate.c3p0.validate">true</property>

<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
true
org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider
5.
20
50
选择1;
真的
10
5.
200
40
但是这没有用,因为C3p0仍然使用默认属性,而不是我在hibernate.cfg.xml中设置的属性,您可以在日志中检查它。所以,我搜索了很多网站,寻找正确的解决方案,最后我找到了这个。删除cfg.xml中的C3p0属性,并在根路径(连同cfg.xml)中创建C3p0-config.xml,并按如下方式设置属性

<c3p0-config>
<default-config> 
<property name="automaticTestTable">con_test</property>
<property name="checkoutTimeout">40</property> 
<property name="idleConnectionTestPeriod">10</property> 
<property name="initialPoolSize">10</property>
<property name="maxPoolSize">20</property> 
<property name="minPoolSize">5</property> 
<property name="maxStatements">50</property>
<property name="preferredTestQuery">SELECT 1;</property>
<property name="acquireRetryAttempts">5</property>
<property name="acquireRetryDelay">200</property>
<property name="maxIdleTime">30</property>
</default-config>
</c3p0-config>

con_试验
40
10
10
20
5.
50
选择1;
5.
200
30
但是如果运行,ORM将使用jdbc连接,而不是C3p0连接池,因为我们应该在hibernate.cfg.xml中添加这些属性

<property name="hibernate.c3p0.validate">true</property>
<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.preferredTestQuery">SELECT 1;</property>
<property name="hibernate.c3p0.testConnectionOnCheckout">true</property>
<property name="hibernate.c3p0.idle_test_period">10</property>
<property name="hibernate.c3p0.acquireRetryAttempts">5</property>
<property name="hibernate.c3p0.acquireRetryDelay">200</property>
<property name="hibernate.c3p0.timeout">40</property>
<property name="hibernate.c3p0.validate">true</property>

<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
true
org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider
现在一切都很好(至少对我来说很好),问题解决了

检查以下参考资料


我希望这能解决您的问题。

是的,这是我今晚测试的下一步。但我想看看我是否还遗漏了什么。谢谢。不起作用-尽管有autoReconnect参数,但仍然以相同的行为失败。几个月后重新访问此功能。”“autoReconnect”没有帮助,但您建议在使用前测试连接是正确的。主要是因为项目启动时,我们还没有承诺使用特定的应用服务器。如果glassfish的连接池达到了标准,我们很乐意尝试。谢谢您的应用程序将不依赖于任何特定的应用程序服务器。最多,您将使用“特定”JNDI语法。您需要做的唯一一件事是让Hibernate从JNDI获取数据源。这确实解决了我的问题,但这是有代价的——我不能再在应用服务器之外使用main()运行单元测试或独立类,因为连接池显然不可用。作为一种解决方法,我维护一个单独的持久化单元,其中包含我的原始设置,并使用thi