最后一次成功从服务器接收到的数据包是在 43417 秒前

Ank*_*ain 3 mysql spring hibernate

我们使用 Spring 4.2.5、Hibernate 4.1.4、MYSql 来构建 REST 服务。我们面临着非常奇怪的问题,无法理解到底发生了什么并出现以下错误:

\n\n

最后一次从服务器成功接收的数据包是在 43417 秒前。最后一次成功发送到服务器的数据包是在 43417 秒前,这比服务器配置的“wait_timeout”值要长。您应该考虑在应用程序中使用之前使连接过期和/或测试连接有效性,增加客户端超时的服务器配置值,或使用 Connector/J 连接属性“autoReconnect=true”来避免此问题。

\n\n

为了获得数据库连接,我们使用基于 java 的配置,如下所示:

\n\n
    @Configuration\n    @EnableTransactionManagement\n    @ComponentScan({ "api.configuration" })\n\npublic class HibernateConfiguration {\n    @Autowired\n    private Environment environment;\n\n    private static final Logger logger = LoggerFactory.getLogger(HibernateConfiguration.class);\n    @Bean\n    public LocalSessionFactoryBean sessionFactory() throws PropertyVetoException {\n        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();\n        sessionFactory.setDataSource(dataSource());\n        sessionFactory.setPackagesToScan(new String[] { "api.domain" });\n        sessionFactory.setHibernateProperties(hibernateProperties());\n        return sessionFactory;\n     }\n\n    @Bean\n    public DataSource dataSource() {\n        DriverManagerDataSource dataSource = new DriverManagerDataSource();\n        dataSource.setDriverClassName("com.mysql.jdbc.Driver");\n        dataSource.setUrl("jdbc:mysql://localhost:3306/test?autoReconnect=true");\n        dataSource.setUsername("test"); \n        dataSource.setPassword("test");\n        return dataSource;\n    }\n\n\n    private Properties hibernateProperties() {\n        Properties properties = new Properties();\n        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");\n        properties.put("hibernate.show_sql", "true");\n        properties.put("hibernate.format_sql", "true");\n        properties.put("hibernate.hbm2ddl.auto", "update");\n        properties.put("connection.autoReconnect",  "true");\n        properties.put("connection.autoReconnectForPools","true");\n        properties.put("connection.is-connection-validation-required", "true");\n\n        return properties;        \n    }\n\n    @Bean\n    public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws PropertyVetoException {\n       LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();\n       em.setDataSource(dataSource());\n       em.setPackagesToScan(new String[] { "api.domain" });\n\n       JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();\n       em.setJpaVendorAdapter(vendorAdapter);\n       em.setJpaProperties(hibernateProperties());\n\n       return em;\n    }\n\n    @Bean\n    public PlatformTransactionManager transactionManager() throws PropertyVetoException{\n       JpaTransactionManager transactionManager = new JpaTransactionManager();\n       transactionManager.setEntityManagerFactory(entityManagerFactory().getObject() );\n       return transactionManager;\n    }\n\n    @Bean\n    public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){\n       return new PersistenceExceptionTranslationPostProcessor();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

谷歌搜索发现我们可以通过添加以下两个属性来解决这个问题:

\n\n
<property name=\xe2\x80\x9dvalidationQuery\xe2\x80\x9d value=\xe2\x80\x9dSELECT 1\xe2\x80\xb3 />\n\n<property name=\xe2\x80\x9dtestOnBorrow\xe2\x80\x9d value=\xe2\x80\x9dtrue\xe2\x80\x9d />\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于我们使用 java 配置而不是基于 XML 的配置,因此我们不知道 在 java 类中为validationQuerytestOnBorrow添加的确切属性名称,因为我们有hibernate.show_sql

\n\n

请建议

\n

Sta*_*sev 6

数据库终止了其会话和物理连接,但客户端直到现在才知道这一点。

我们先来了解一下问题所在。java.sql.Connection是一个Java对象,代表一些MySQL服务器连接。这是通过 TCP 进行的客户端-服务器通信。如果服务器连接断开或 TCP 连接断开,客户端不知道这一点,因此 Java 对象仍然存在,但无法再与服务器通信。在您的情况下,如果过时的连接闲置一段时间(默认情况下为 8 小时),MySQL 服务器将终止它们。

如果您创建一个裸驱动程序连接,这种情况将非常罕见 - 这种连接会在使用很短一段时间后创建并关闭。在你的示例中,事情非常混乱 - 你同时使用 SessionFactory 和 EntityManagerFactory ,看起来你使用裸连接(并且很难用裸连接得到这个错误),所以我不会对此发表评论。

您找到的选项不适用于 Hibernate。它们用于生产就绪的数据库池之一(我假设是 Tomcat JDBC 池)。因此,您应该使用该池(或另一个池,如 C3P0)并将其配置为定期检查连接 - 这样,从 MySQL 服务器的角度来看,连接就不会过时。以下是C3P0配置的示例:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="jdbcUrl" value="jdbc:h2:mem:qalatraining;DB_CLOSE_DELAY=-1"/>
    <property name="driverClass" value="org.h2.Driver"/>
    <property name="user" value="sa"/>
    <property name="password" value=""/>
    <property name="maxPoolSize" value="10"/>
    <property name="idleConnectionTestPeriod" value="3600"/>
    <property name="testConnectionOnCheckin" value="true"/>
</bean>
Run Code Online (Sandbox Code Playgroud)

现在您应该将此数据源传递给 Hibernate:

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>
Run Code Online (Sandbox Code Playgroud)

配置数据库池还有很多内容,您可以在此处找到详细信息,并在此处找到工作示例。