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;
}
}
}
}
任何想法将不胜感激。谢谢
- 创建一个 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;
}
}
Bean 创建
@Bean(name = "dbConnection")
public Connection dbConn() throws SQLException {
return new DBConnection(username, password, connectionString);
}
现在,您可以自动装配 DBConnection 并且对于任何 SQL 执行,您可以从此 bean 获取连接 - dbConnection.getConn() 用于执行任何 SQL声明。
在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
我有一个 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;
}
}
}
}
任何想法将不胜感激。谢谢
- 创建一个 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;
}
}
Bean 创建
@Bean(name = "dbConnection") public Connection dbConn() throws SQLException { return new DBConnection(username, password, connectionString); }
现在,您可以自动装配 DBConnection 并且对于任何 SQL 执行,您可以从此 bean 获取连接 - dbConnection.getConn() 用于执行任何 SQL声明。
在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