Помогите мне избежать таймаута соединения с JPA, Hibernate & MySQL

Я использую 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; вполне возможно, что мне не хватает части, которая на самом деле говорит о спящем режиме "эй, используйте c3p0".

Я собираюсь попробовать предложение о том, что прямо там в сообщении об ошибке: добавьте autoReconnect=true к моему URL-адресу JDBC, но на данный момент это действительно начинает ощущаться как развитие грузового культа. Я был бы признателен за некоторые рекомендации относительно надлежащего способа решения этой проблемы. Трудно отлаживать, потому что тестовый цикл эффективно "запускает его в одночасье, смотрите, что происходит утром".

Я должен, вероятно, упомянуть, как я действительно использую подключения в своем приложении. У меня есть настраиваемый Servlet Filter, который перехватывает все запросы. Он создает EntityManager, сохраняет его в ThreadLocal и закрывается фильтром в блоке catch/finally. Все мои сущности получают ссылку на EntityManager из ThreadLocal.

Весьма возможно, что мой фильтр виноват, но, как это только кажется, что происходит после простоя, я подозреваю, что что-то еще не так. Я собираюсь перейти к Seam/Weld, когда у меня есть шанс перевести дыхание, но пока я полагаюсь на этот фильтр.

Изменить: здесь решение TL; DR:

  • используйте пул соединений с контейнером, если вы можете (спасибо, @partenon)
  • убедитесь, что ваш пул соединений использует проверку соединения (спасибо, @matt b)

В моем случае мне пришлось зайти в консоль Glassfish в разделе Ресурсы/JDBC/Пулы соединений, вкладку "Дополнительно", а затем включить проверку подключения:

enter image description here

Это был действительно важный шаг. Вероятно, вы также захотите установить Validate At Most Once на что-то разумное, скажем 100 секунд. Если вы используете C3P0 или аналогичный, убедитесь, что вы настраиваете idle_test_period и preferredTestQuery.

Что бы вы ни делали, важно проверить свои изменения, чтобы убедиться, что они имеют желаемый эффект. Чтобы сделать тайм-аут быстрее в MySQL, вы можете временно установить wait_timeout на что-то низкое, как 30 секунд, отредактировав my.cnf. Это была огромная помощь в отладке этой проблемы, так как она позволяла мне проверять изменения в секундах, а не на часах.

Ответ 1

Вы должны рассмотреть вопрос об истечении срока действия и/или проверять достоверность подключения перед использованием в вашем приложении, увеличивая настроенные сервером значения для тайм-аутов клиента или используя свойство соединения Connector/J "autoReconnect = true", чтобы избежать этой проблемы.

Просто в темноте, но взглянули ли вы на настройку свойства autoReconnect=true в своем драйвере JDBC? Или рассмотрите возможность отключения настройки на стороне сервера для тайм-аутов подключения клиентов.

Ответ 2

Я думаю, что реальный вопрос: почему вы используете механизм объединения внешних соединений вместо того, чтобы использовать собственный пул Glassfish? Ваш сервер приложений лучше подходит для предоставления такого сервиса вашему приложению. "Внешние" механизмы объединения соединений лучше подходят для автономных приложений, а не для приложений в контейнере.

Ответ 3

Я думаю, что свойство установить период тестирования соединения idle_test_period, а не idleTestPeriod в соответствии с документацией C3P0 здесь. Поэтому вы должны использовать:

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

вместо.

Ответ 4

У меня возникала такая же проблема, и нам потребовалось время, чтобы выяснить решение.

Я использую Hibernate 4.0.1 и mysql 5.1 (без spring), и я столкнулся с этой проблемой. Сначала убедитесь, что вы правильно сконфигурировали банки 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>

Но это бесполезно, потому что C3p0 по-прежнему использует свойства по умолчанию, а не свойства, которые я задал в файле hibernate.cfg.xml, вы можете проверить его в журналах. Итак, я искал многие веб-сайты для правильного решения, и, наконец, я придумал это. удалите свойства C3p0 в cfg.xml и создайте c3p0-config.xml в корневом пути (вместе с cfg.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>

но если вы запустите, 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>

теперь все работает нормально (по крайней мере, это сработало для меня), и проблема решена.

проверьте ссылки для ссылок.

http://www.mchange.com/projects/c3p0/index.html#configuring_connection_testing

https://community.jboss.org/wiki/HowToConfigureTheC3P0ConnectionPool

Надеюсь, это решит вашу проблему.