Spring Boot JPA - configurando a reconexão automática

107

Eu tenho um pequeno aplicativo da web Spring Boot JPA. Ele é implantado no Amazon Beanstalk e usa um Amazon RDS para dados persistentes. No entanto, não é usado com frequência e, portanto, falha depois de um tempo com este tipo de exceção:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: O último pacote recebido com sucesso do servidor foi 79.870.633 milissegundos atrás.
O último pacote enviado com sucesso ao servidor foi 79.870.634 milissegundos atrás. é mais longo do que o valor configurado pelo servidor de 'wait_timeout'. Você deve considerar expirar e / ou testar a validade da conexão antes de usar em seu aplicativo, aumentando os valores configurados do servidor para tempos limite do cliente ou usando a propriedade de conexão Conector / J 'autoReconnect = true' para evitar esse problema.

Não tenho certeza de como definir essa configuração e não consigo encontrar informações sobre ela em http://spring.io (um site muito bom, no entanto). Quais são algumas idéias ou indicações para informações?

stoffer
fonte
Use-o para imprimir DataSourcee verificar suas propriedades. stackoverflow.com/a/36586630/148844 Spring Boot não irá configurar automaticamente o DataSourcese você tiver algum @Beansque defina a DataSource. docs.spring.io/spring-boot/docs/1.5.16.RELEASE/reference/…
Chloe

Respostas:

141

Presumo que o boot esteja configurando o DataSourcepara você. Neste caso, e já que você está usando MySQL, você pode adicionar o seguinte ao seu application.propertiesaté 1,3

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

Como djxak observado no comentário, 1.4+ define espaços de nomes específicos para as quatro ligações piscinas da mola de arranque suporta: tomcat, hikari, dbcp, dbcp2( dbcpé suspenso a partir de 1.5). Você precisa verificar qual pool de conexão está usando e se esse recurso é compatível. O exemplo acima era para tomcat, então você teria que escrevê-lo da seguinte maneira em 1.4+:

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

Observe que o uso de nãoautoReconnect é recomendado :

O uso deste recurso não é recomendado, porque tem efeitos colaterais relacionados ao estado da sessão e consistência de dados quando os aplicativos não manipulam SQLExceptions corretamente, e só foi projetado para ser usado quando você não consegue configurar seu aplicativo para lidar com SQLExceptions resultantes de conexões mortas e obsoletas corretamente.

Stephane Nicoll
fonte
8
isso porque harmonizamos a maneira como escrevemos as chaves na documentação. Nós sempre usou uma relaxada aglutinante assim ambos spring.datasource.testOnBorrowe spring.datasource.test-on-borrowvai funcionar muito bem. Verifique a documentação para mais detalhes.
Stephane Nicoll
17
Uma vez que pode confundir outras pessoas: SELECT 1garante que a conexão foi testada antes de ser entregue ao aplicativo. Com o uso testOnBorrow = true, os objetos serão validados antes de serem emprestados do pool. Se o objeto falhar na validação, ele será descartado do pool e tentará pegar outro emprestado. NOTA - para um valor verdadeiro ter algum efeito, o parâmetro validationQuery deve ser definido como uma string não nula.
Rick
14
Aviso! Na Primavera de inicialização 1.4+ isso foi mudado : não foi definida novos namespaces específicos para os quatro conexões piscinas suportes de mola: tomcat, hikari, dbcp, dbcp2. Assim, por exemplo, para tomcat-jdbcpool de conexão, as propriedades devem ser: spring.datasource.tomcat.testOnBorrow=truee spring.datasource.tomcat.validationQuery=SELECT 1.
Ruslan Stelmachenko
1
Se eu estiver configurando duas fontes de dados diferentes, como fornecer essas configurações? Preciso fornecer esta configuração para a fonte de dados, como 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 Ou há algo mais a seguir ??
Nitish Kumar
2
Aviso! Se você definir qualquer DataSource @Bean em seu aplicativo, o Spring Boot não configurará o pool. docs.spring.io/spring-boot/docs/1.5.16.RELEASE/reference/… If you define your own DataSource bean, auto-configuration will not occur. Segui um guia para OAuth2 e tinha @Bean(name = "OAuth") public DataSource secondaryDataSource()...e não foi configurado automaticamente nem em uso testOnBorrow.
Chloe de
28

As sugestões acima não funcionaram para mim. O que realmente funcionou foi a inclusão das seguintes linhas no application.properties

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

Você pode encontrar a explicação aqui

Soumya
fonte
5
O link que você adicionou diz: Se a conexão com o banco de dados ficar inativa por mais de 8 horas, ela será fechada automaticamente e o erro acima ocorrerá. Portanto, sua solução é não deixar a conexão inativa por mais tempo. Existe uma maneira de me conectar ao servidor SQL depois de reiniciado?
Akeshwar Jha
9

A configuração spring.datasource.tomcat.testOnBorrow=trueem application.properties não funcionou.

A configuração programática como abaixo funcionou sem problemas.

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);
}
Quem sou eu
fonte
1
Se você está declarando uma fonte de dados personalizada, pode ser porque está tentando usar o spring default .tomcat. Portanto, se você criar um bean DataSource customizado, adicione @ConfigurationProperties (prefix = "spring.datasource.tomcat") ao bean DataSource e então deve permitir que você os defina nas propriedades do aplicativo. Meu exemplo .. @Bean (name = "managementDataSource") @ConfigurationProperties (prefix = "management.datasource") public DataSource dataSource () {return DataSourceBuilder.create (). Build (); } management.datasource.test-on-borrow = true
Justin
8

Acabei de mudar para o Spring Boot 1.4 e descobri que essas propriedades foram renomeadas:

spring.datasource.dbcp.test-while-idle=true
spring.datasource.dbcp.time-between-eviction-runs-millis=3600000
spring.datasource.dbcp.validation-query=SELECT 1
Jose Jurado
fonte
2
Os nomes são equivalentes. Veja a seção sobre nomenclatura de propriedades nos documentos do Spring Boot .
Stephen Harrison
@StephenHarrison: observe o prefixo dbcp. * Adicionado em 1.4, vinculação relaxada não se aplica neste caso.
YM
1
@Pawel: dependendo de qual implementação de pool está disponível em seu projeto, pode não ser as propriedades dbcp. * Para você, consulte Spring boot com SQL e as propriedades de fonte de dados
YM
4

a resposta de whoami é a correta. Usando as propriedades conforme sugerido, não consegui fazer isso funcionar (usando Spring Boot 1.5.3.RELEASE)

Estou adicionando minha resposta, pois é uma classe de configuração completa, então pode ajudar alguém usando 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;
    }
}
naoru
fonte
Você sabe por que esse código personalizado é necessário e por que o Spring não apenas lê essas propriedades do arquivo de propriedades? Eu tenho várias propriedades de fonte de dados em meu arquivo e ele lê todas as outras sem problemas.
Tio Cabelo Comprido
3

Eu tenho um problema semelhante. Spring 4 e Tomcat 8. Eu resolvo o problema com a configuração do Spring

<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>

Eu testei. Isso funciona bem! Essas duas linhas fazem tudo para se reconectar ao banco de dados:

<property name="testOnBorrow" value="true" />
<property name="validationQuery" value="SELECT 1" />
grep
fonte
3

Caso alguém esteja usando DataSource personalizado

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

As propriedades devem ser semelhantes às seguintes. Observe @ConfigurationProperties com prefixo. O prefixo é tudo antes do nome real da propriedade

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

Uma referência para Spring Versão 1.4.4.RELEASE

Justin
fonte
2

Como algumas pessoas já apontaram, spring-boot 1.4+, tem namespaces específicos para os quatro pools de conexões. Por padrão, hikaricp é usado no spring-boot 2+. Portanto, você terá que especificar o SQL aqui. O padrão é SELECT 1. Aqui está o que você precisa para o DB2, por exemplo: spring.datasource.hikari.connection-test-query=SELECT current date FROM sysibm.sysdummy1

Advertência : se o seu driver suportar JDBC4, recomendamos enfaticamente não definir esta propriedade. Isso é para drivers "legados" que não suportam a API JDBC4 Connection.isValid (). Esta é a consulta que será executada pouco antes de uma conexão do pool ser fornecida a você para validar se a conexão com o banco de dados ainda está ativa. Novamente, tente executar o pool sem essa propriedade, o HikariCP registrará um erro se o seu driver não for compatível com JDBC4 para informá-lo. Padrão: nenhum

code4kix
fonte
0

Para aqueles que desejam fazer isso em YAML com várias fontes de dados, há uma ótima postagem no blog sobre isso: https://springframework.guru/how-to-configure-multiple-data-sources-in-a-spring-boot -inscrição/

Basicamente, ele diz que vocês dois precisam configurar as propriedades da fonte de dados e a fonte de dados assim:

@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();
}

Não se esqueça de remover @Primaryde outras fontes de dados.

enesaltinok
fonte