123

I have a nice little Spring Boot JPA web application. It is deployed on Amazon Beanstalk and uses an Amazon RDS for persisting data. It is however not used that often and therefore fails after a while with this kind of exception:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 79,870,633 milliseconds ago.
The last packet sent successfully to the server was 79,870,634 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.

I am not sure how to configure this setting and can not find information on it on http://spring.io (a very good site though). What are some ideas or pointers to information?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
stoffer
  • 2,317
  • 2
  • 18
  • 24
  • Use this to print out your `DataSource` and verify its properties. https://stackoverflow.com/a/36586630/148844 Spring Boot will not auto-configure the `DataSource` if you have any `@Beans` which define a `DataSource`. https://docs.spring.io/spring-boot/docs/1.5.16.RELEASE/reference/htmlsingle/#boot-features-connect-to-production-database – Chloe Oct 20 '18 at 02:25

9 Answers9

157

I assume that boot is configuring the DataSource for you. In this case, and since you are using MySQL, you can add the following to your application.properties up to 1.3

spring.datasource.testOnBorrow=true
spring.datasource.validationQuery=SELECT 1

As djxak noted in the comment, 1.4+ defines specific namespaces for the four connections pools Spring Boot supports: tomcat, hikari, dbcp, dbcp2 (dbcp is deprecated as of 1.5). You need to check which connection pool you are using and check if that feature is supported. The example above was for tomcat so you'd have to write it as follows in 1.4+:

spring.datasource.tomcat.testOnBorrow=true 
spring.datasource.tomcat.validationQuery=SELECT 1

Note that the use of autoReconnect is not recommended:

The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don't handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly.

Stephane Nicoll
  • 31,977
  • 9
  • 97
  • 89
  • 9
    that's because we've harmonized the way we write keys in documentation. We always used a _relaxed_ binder so both `spring.datasource.testOnBorrow` and `spring.datasource.test-on-borrow` will work just fine. Check [the documentation](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-relaxed-binding) for more details. – Stephane Nicoll Feb 03 '15 at 11:36
  • 19
    Since it may confuse others: `SELECT 1` guarantees that the connection has been tested before it's handed to the application. By using `testOnBorrow = true`, the objects will be validated before being borrowed from the pool. If the object fails to validate, it will be dropped from the pool, and will attempt to borrow another. NOTE – for a true value to have any effect, the validationQuery parameter must be set to a non-null string. – Rick Jun 23 '15 at 02:53
  • 16
    **Warning!** In Spring Boot 1.4+ this was [changed](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes#datasource-binding): there was defined new specific namespaces for the four connections pools spring supports: `tomcat`, `hikari`, `dbcp`, `dbcp2`. So, for example, for `tomcat-jdbc` connection-pool, the properties should be: `spring.datasource.tomcat.testOnBorrow=true` and `spring.datasource.tomcat.validationQuery=SELECT 1`. – Ruslan Stelmachenko Jan 18 '17 at 13:29
  • 2
    If I am myself configuring two different datasource then how do I provide these configuration? Do I need to provide this configuration for both the datasource like spring.datasource.mydatasource1.tomcat.testOnBorrow=true spring.datasource.mydatasource1.tomcat.validationQuery=SELECT 1 spring.datasource.mydatasource2.tomcat.testOnBorrow=true spring.datasource.mydatasource2.tomcat.validationQuery=SELECT 1 Or there is something else to follow?? – Nitish Kumar Dec 27 '17 at 10:00
  • 3
    **Warning!** If you define any DataSource @Bean in your app, then Spring Boot *won't* configure the pool. https://docs.spring.io/spring-boot/docs/1.5.16.RELEASE/reference/htmlsingle/#boot-features-connect-to-production-database `If you define your own DataSource bean, auto-configuration will not occur.` I followed a guide for OAuth2 and had `@Bean(name = "OAuth") public DataSource secondaryDataSource()...` and it was not auto-configured nor using `testOnBorrow`. – Chloe Oct 20 '18 at 02:09
  • 1
    Thanks Chloe but that's what happening for pretty much everything. If you define your own component, Spring Boot will detect it and back off. – Stephane Nicoll Nov 05 '18 at 15:14
  • 2
    If you are using hikari, which is the default connection pool in `Spring Boot 2`, you do not need to test the connection before it is handed to you, as hikari does it. Quote from [HikariCP github page](https://github.com/brettwooldridge/HikariCP): `...Connection.isValid() API. This is the query that will be executed just before a connection is given to you from the pool to validate that the connection to the database is still alive.` – jumping_monkey Oct 06 '20 at 01:37
  • What I have to use if I have Hikari with spring boot, as I am getting this error on the production server? – Raman Joshi Jun 16 '21 at 12:42
30

The above suggestions did not work for me. What really worked was the inclusion of the following lines in the application.properties

spring.datasource.testWhileIdle = true
spring.datasource.timeBetweenEvictionRunsMillis = 3600000
spring.datasource.validationQuery = SELECT 1

You can find the explanation out here

Soumya
  • 1,833
  • 5
  • 34
  • 45
  • 5
    The link you've added says **If the database connection is inactive for more than 8 hours it is automatically closed and the error above will happen.** So, your solution is not to let the connection remain inactive for longer-durations. Is there a way I can connect to the SQL server after it has been restarted? – Akeshwar Jha Sep 15 '17 at 07:12
  • Is it possible to set 28,800,000-1 instead of 3,600,000 to avoid timeout, according to MySQL documentation timeout? – Askar Jan 07 '22 at 17:15
10

Setting spring.datasource.tomcat.testOnBorrow=true in application.properties didn't work.

Programmatically setting like below worked without any issues.

import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;    

@Bean
public DataSource dataSource() {
    PoolProperties poolProperties = new PoolProperties();
    poolProperties.setUrl(this.properties.getDatabase().getUrl());         
    poolProperties.setUsername(this.properties.getDatabase().getUsername());            
    poolProperties.setPassword(this.properties.getDatabase().getPassword());

    //here it is
    poolProperties.setTestOnBorrow(true);
    poolProperties.setValidationQuery("SELECT 1");

    return new DataSource(poolProperties);
}
whoami
  • 1,517
  • 1
  • 21
  • 29
  • 1
    If you are declaring a custom datasource it might be because you are trying to use the spring default .tomcat. So if you create a custom Datasource bean then add the @ConfigurationProperties(prefix = "spring.datasource.tomcat") to the DataSource bean and then it should allow you to set them in the application properties. My Example.. @Bean(name = "managementDataSource") @ConfigurationProperties(prefix = "management.datasource") public DataSource dataSource() { return DataSourceBuilder.create().build(); } management.datasource.test-on-borrow=true – Justin Mar 29 '18 at 14:51
8

I just moved to Spring Boot 1.4 and found these properties were renamed:

spring.datasource.dbcp.test-while-idle=true
spring.datasource.dbcp.time-between-eviction-runs-millis=3600000
spring.datasource.dbcp.validation-query=SELECT 1
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Jose Jurado
  • 301
  • 4
  • 7
  • 2
    The names are equivalent. See the section on property naming in the [Spring Boot docs](http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-relaxed-binding). – Stephen Harrison Sep 30 '16 at 19:43
  • @StephenHarrison : notice the dbcp.* prefix added in 1.4, relaxed binding doesn't apply in this case. – Y.M. Nov 29 '16 at 14:01
  • 1
    @Pawel : depending on which pooling implementation is available in your project, it might not be the dbcp.* properties for you, see [Spring boot with SQL](http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-connect-to-production-database) and the corresponding [Datasource properties](http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html) – Y.M. Nov 29 '16 at 14:11
4

whoami's answer is the correct one. Using the properties as suggested I was unable to get this to work (using Spring Boot 1.5.3.RELEASE)

I'm adding my answer since it's a complete configuration class so it might help someone using Spring Boot:

@Configuration
@Log4j
public class SwatDataBaseConfig {

    @Value("${swat.decrypt.location}")
    private String fileLocation;

    @Value("${swat.datasource.url}")
    private String dbURL;

    @Value("${swat.datasource.driver-class-name}")
    private String driverName;

    @Value("${swat.datasource.username}")
    private String userName;

    @Value("${swat.datasource.password}")
    private String hashedPassword;

    @Bean
    public DataSource primaryDataSource() {
        PoolProperties poolProperties = new PoolProperties();
        poolProperties.setUrl(dbURL);
        poolProperties.setUsername(userName);
        poolProperties.setPassword(password);
        poolProperties.setDriverClassName(driverName);
        poolProperties.setTestOnBorrow(true);
        poolProperties.setValidationQuery("SELECT 1");
        poolProperties.setValidationInterval(0);
        DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
        return ds;
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
naoru
  • 2,149
  • 5
  • 34
  • 58
  • Do you know why this custom code is needed and why Spring won't just read these properties from the properties file? I have several datasource properties in my file and it reads all the rest of them without a problem. – Uncle Long Hair Mar 01 '19 at 17:39
3

I have similar problem. Spring 4 and Tomcat 8. I solve the problem with Spring configuration

<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
    <property name="initialSize" value="10" />
    <property name="maxActive" value="25" />
    <property name="maxIdle" value="20" />
    <property name="minIdle" value="10" />
     ...
    <property name="testOnBorrow" value="true" />
    <property name="validationQuery" value="SELECT 1" />
 </bean>

I have tested. It works well! This two line does everything in order to reconnect to database:

<property name="testOnBorrow" value="true" />
<property name="validationQuery" value="SELECT 1" />
grep
  • 5,465
  • 12
  • 60
  • 112
3

In case anyone is using custom DataSource

@Bean(name = "managementDataSource")
@ConfigurationProperties(prefix = "management.datasource")
public DataSource dataSource() {
    return DataSourceBuilder.create().build();
}

Properties should look like the following. Notice the @ConfigurationProperties with prefix. The prefix is everything before the actual property name

management.datasource.test-on-borrow=true
management.datasource.validation-query=SELECT 1

A reference for Spring Version 1.4.4.RELEASE

Justin
  • 866
  • 1
  • 13
  • 29
3

As some people already pointed out, spring-boot 1.4+, has specific namespaces for the four connections pools. By default, hikaricp is used in spring-boot 2+. So you will have to specify the SQL here. The default is SELECT 1. Here's what you would need for DB2 for example: spring.datasource.hikari.connection-test-query=SELECT current date FROM sysibm.sysdummy1

Caveat: If your driver supports JDBC4 we strongly recommend not setting this property. This is for "legacy" drivers that do not support the JDBC4 Connection.isValid() API. This is the query that will be executed just before a connection is given to you from the pool to validate that the connection to the database is still alive. Again, try running the pool without this property, HikariCP will log an error if your driver is not JDBC4 compliant to let you know. Default: none

code4kix
  • 3,937
  • 5
  • 29
  • 44
0

For those who want to do it from YAML with multiple data sources, there is a great blog post about it: https://springframework.guru/how-to-configure-multiple-data-sources-in-a-spring-boot-application/

It basically says you both need to configure data source properties and datasource like this:

@Bean
@Primary
@ConfigurationProperties("app.datasource.member")
public DataSourceProperties memberDataSourceProperties() {
    return new DataSourceProperties();
}

@Bean
@Primary
@ConfigurationProperties("app.datasource.member.hikari")
public DataSource memberDataSource() {
    return memberDataSourceProperties().initializeDataSourceBuilder()
            .type(HikariDataSource.class).build();
}

Do not forget to remove @Primary from other datasources.

RobC
  • 22,977
  • 20
  • 73
  • 80
enesaltinok
  • 55
  • 1
  • 7