Java/Spring - 重新连接到数据库

Java/Spring - reconnect to DB

我有一个 Java/Spring/Hibernate 项目,它连接到 Vertica 数据库。有时,连接会因某些环境问题而中断,我正在尝试在 运行 时间内重新建立连接。问题是原始连接,即 @Autowired 到存储库的连接没有得到更新,并且仍然指向原始的“死”连接。

原始连接在服务器启动期间通过配置创建为@Bean。

配置:

package com.myproject.configs
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import lombok.extern.slf4j.Slf4j;

@Configuration
@Slf4j
public class dbConfig {


@Value("${db.jdbcUrl}")
private String connectionString;

@Value("${db.username}")
private String username;

@Value("${db.password}")
private String password;

@Bean(name = "dbConnection", destroyMethod = "close")
public Connection dbConn() throws SQLException {
   Properties myProp = new Properties();
   myProp.put("user", username);
   myProp.put("password", password);

   Connection conn;
   try {
      conn = DriverManager.getConnection(connectionString, myProp);
      return conn;
    } catch (SQLException e) {
      log.error("Cannot establish DB connection");
      throw e;
   }
}

}

用法:

    @Repository
    @Slf4j
    public class SomeDbRepository {

    @Autowired
    Connection dbConnection;
    ...

现在,我需要监视传入的 API 请求是否会真正到达数据库,所以我想在拦截器中检查所有传入的请求,然后如果连接没有响应,则重新建立它.

因此,我使用上下文获取 bean 并再次 运行 dbConn():

  package com.myproject.interceptors;
    import com.myproject.configs.DbConfig;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.sql.*;
    @Component
    @Slf4j
    public class DbConnectionCheckInterceptor extends HandlerInterceptorAdapter { 
    @Autowired
    Connection dbConnection;
    @Autowired
    ApplicationContext context;

   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try {
           Statement stmt = dbConnection.createStatement();
           ResultSet rs = stmt.executeQuery("SELECT 1;");
           rs.next();
           return true;
       } catch (SQLException e) {
           try {
               DbConfig dbConfig = context.getBean(DbConfig.class);
    // *** here i am trying to re-establish the connection by accessing the bean through context ***
               dbConfig.dbConn();
               return true;
           } catch (Exception ex) {
               log.error("Couldn't connect to DB", ex);
               return false;
           }
       }
   }
}

任何想法将不胜感激。谢谢

  1. 创建一个 Java Class - DBConnection

:

public class DBConnection {
private Connection conn;
private String connString;
private Properties myProp;    
public Connection getConn() {
    return conn;
}

public DBConnection(String userName, String pwd, String connString) {
    myProp = new Properties();
    myProp.put("user", username);
    myProp.put("password", password);
    this.connString = connString;
    try{
        setConn();
    }catch (Exception e){
        log.error("DB connection failed", e);
    }
}

public void setConn() throws SQLException {
    try {
        //Properly close the existing connection before procuring new one. Check if conn not null and close accordingly 
        conn = DriverManager.getConnection(connectionString, myProp);
    } catch (SQLException e) {
        log.error("Cannot establish DB connection", e);
        throw e;
    }
}
  1. Bean 创建

     @Bean(name = "dbConnection")
     public Connection dbConn() throws SQLException {
        return new DBConnection(username, password, connectionString);
     }
    
  2. 现在,您可以自动装配 DBConnection 并且对于任何 SQL 执行,您可以从此 bean 获取连接 - dbConnection.getConn() 用于执行任何 SQL声明。

  3. 在preHandle中,你可以简单的做dbConnection.setConn()。任何 SQL 语句执行,将尝试引用相同的 bean - dbConnection.getConn() 但其中的 conn 对象会被刷新。

感谢您的回答。最终,我们通过将连接配置 bean 重构为使用 DataSource 和 JdbcTemplate + hikari 连接池的方法解决了这个问题。现在,Hikari 负责连接并在需要时重新连接到数据库。

配置bean:

@Configuration
@Slf4j
public class VerticaConfig {

    @Value("${spring.datasource.jdbcUrl}")
    private String connectionString;

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

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

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


    @Bean(name = "verticaDataSource", destroyMethod = "close")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource verticaDataSource() {
        return DataSourceBuilder.create()
                .driverClassName(driverClassName)
                .url(connectionString)
                .username(username)
                .password(password)
                .build();
    }

    @Bean(name = "verticaJdbcTemplate")
    public NamedParameterJdbcTemplate verticaJdbcTemplate() {
        return new NamedParameterJdbcTemplate(verticaDataSource());
    }
}

光配置:

spring:
  datasource:
    driver-class-name: com.vertica.jdbc.Driver
    jdbcUrl:  jdbc:vertica://abc
    username: ***
    password: ***
    # hikari config
    maxLifetime: 300000 #5 min default 30 min
    poolName: my-connection-pool
    maximumPoolSize: 5
    minimum-idle: 3
    connectionTimeout: 59000
    validationTimeout: 10000
    idleTimeout: 240000 #4 min  default 10 min
    leakDetectionThreshold: 59000