【问题标题】:Java/Spring - reconnect to DBJava/Spring - 重新连接到数据库
【发布时间】:2021-06-06 12:19:31
【问题描述】:

我有一个 Java/Spring/Hibernate 项目,该项目与 Vertica DB 有连接。有时,由于某些环境问题,连接会中断,我正在尝试在运行时重新建立连接。问题是原始连接,即@Autowired into repositories 没有得到更新,并且仍然指向原始的“死”连接。

在服务器启动期间,原始连接通过配置创建为@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;
           }
       }
   }
}

任何想法都会受到赞赏。谢谢。

【问题讨论】:

  • 如果你需要刷新这个连接,你可以取消Using spring bean,也许你可以使用静态方法来获取连接对象(而不是bean),它有你的重新连接逻辑(如果required) 否则返回预填充的数据库连接对象。

标签: java spring runtime database-connection autowired


【解决方案1】:

感谢您的回答。最终,我们通过将连接配置 bean 重构为使用带有 JdbcTemplate + hikari 连接池的 DataSource 的方法解决了这个问题。现在,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());
    }
}

Hikari 配置:

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

【讨论】:

    【解决方案2】:
    1. 创建 Java 类 - 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(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 对象会被刷新。

    【讨论】:

      猜你喜欢
      • 2020-03-13
      • 2017-09-05
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-12
      • 2011-08-27
      • 2016-12-10
      相关资源
      最近更新 更多