我有一个简单的类,可以搜索 PostgreSQL 中的所有表并为它们生成
SELECT
查询。工作正常但需要有关关闭 java.sql.Connection 的建议
我希望我的对象在实例仍然存在时保持数据库连接。为此,我创建了一个私有 Connection connection
字段,这样与数据库的连接不会在每个方法被调用后(在一个会话期间)建立和关闭,而是创建一次并在主线程停止后或 bean 之后关闭被处置。我尝试使用 @PreDestroy
并实现一个 DisposableBean
接口,但没有看到任何明确的结果。那么关闭这种连接的正确方法是什么?
QueryBuilder 类
@Component
public class PostgreSQLQueryBuilder implements SQLQueryBuilder{
private DatabaseMetaData metaData;
private Connection connection; // I need to close this somehow
@Autowired
public PostgreSQLQueryBuilder(DataSource dataSource) {
try {
this.connection = dataSource.getConnection();
this.metaData = connection.getMetaData();
} catch (SQLException e) {
System.out.println("Connection exception");
e.printStackTrace();
}
}
@Override
public String queryForTable(String tableName){
String resultQuery = null;
List<String> columnList = new ArrayList<>();
if (getTables().contains(tableName)) {
try (ResultSet columns = metaData.getColumns(null, null, tableName, null)) {
//some logic
return resultQuery;
} catch (SQLException e) {
System.out.println("Exception");
e.printStackTrace();
}
}
return resultQuery;
}
@Override
public List<String> getTables() {
List<String> tables = new ArrayList<>();
try (ResultSet schemaRs = metaData.getSchemas()) {
while (schemaRs.next()) {
String currentSchema = schemaRs.getString(1);
try (ResultSet tableRS = metaData.getTables(null, currentSchema, "%", new String[]{"TABLE", "SYSTEM TABLE"})) {
while (tableRS.next()) {
tables.add(tableRS.getString("TABLE_NAME"));
}
}
}
} catch (SQLException e) {
System.out.println("Exception");
e.printStackTrace();
}
return tables;
}
// I tried this
@PreDestroy
private void closeConnections(){
try{
this.connection.close()
//this is not printed
System.out.println("Connection closed")
}catch(Exception e){
//some logic
}
}
}
测试类:
public class SQLQueryExtenderTest {
public static void main(String[] args) throws Exception{
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
applicationContext.start();
SQLQueryBuilder queryBuilder = applicationContext.getBean(SQLQueryBuilder.class);
List<String> tables = queryBuilder.getTables();
System.out.println(tables);
System.out.println(queryBuilder.queryForTable("some_table"));
System.out.println(queryBuilder.queryForTable("person"));
System.out.println(queryBuilder.queryForTable(null));
}
//I expect the connection to be closed at this point
}
当不再需要 JDBC 连接等资源时,明确关闭它们是一种很好的做法。关闭资源有助于将其释放回资源池,防止资源泄漏,节约系统资源。
在您的情况下,您可以在用@PreDestroy 注释的 closeConnections() 方法中关闭连接资源。这个方法应该在 bean 被销毁时调用。
但是,您提到没有调用 closeConnections() 方法。这可能是因为当应用程序上下文关闭时 PostgreSQLQueryBuilder bean 没有被销毁。要解决此问题,您可以使用 registerShutdownHook() 方法向 Spring ApplicationContext 注册一个关闭挂钩。
这是您的代码的更新版本,可在 bean 被销毁时关闭连接:
QueryBuilder类:
@Component
public class PostgreSQLQueryBuilder implements SQLQueryBuilder, DisposableBean {
private DatabaseMetaData metaData;
private Connection connection;
@Autowired
public PostgreSQLQueryBuilder(DataSource dataSource) {
try {
this.connection = dataSource.getConnection();
this.metaData = connection.getMetaData();
} catch (SQLException e) {
System.out.println("Connection exception");
e.printStackTrace();
}
}
@Override
public String queryForTable(String tableName){
String resultQuery = null;
List<String> columnList = new ArrayList<>();
if (getTables().contains(tableName)) {
try (ResultSet columns = metaData.getColumns(null, null, tableName, null)) {
//some logic
return resultQuery;
} catch (SQLException e) {
System.out.println("Exception");
e.printStackTrace();
}
}
return resultQuery;
}
@Override
public List<String> getTables() {
List<String> tables = new ArrayList<>();
try (ResultSet schemaRs = metaData.getSchemas()) {
while (schemaRs.next()) {
String currentSchema = schemaRs.getString(1);
try (ResultSet tableRS = metaData.getTables(null, currentSchema, "%", new String[]{"TABLE", "SYSTEM TABLE"})) {
while (tableRS.next()) {
tables.add(tableRS.getString("TABLE_NAME"));
}
}
}
} catch (SQLException e) {
System.out.println("Exception");
e.printStackTrace();
}
return tables;
}
@PreDestroy
private void closeConnections(){
try{
this.connection.close();
System.out.println("Connection closed");
}catch(Exception e){
//some logic
}
}
@Override
public void destroy() throws Exception {
closeConnections();
}
}
测试类:
public class SQLQueryExtenderTest {
public static void main(String[] args) throws Exception{
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
applicationContext.start();
applicationContext.registerShutdownHook(); // Register a shutdown hook to close resources
SQLQueryBuilder queryBuilder = applicationContext.getBean(SQLQueryBuilder.class);
List<String> tables = queryBuilder.getTables();
System.out.println(tables);
System.out.println(queryBuilder.queryForTable("some_table"));
System.out.println(queryBuilder.queryForTable("person"));
System.out.println(queryBuilder.queryForTable(null));
}
}
在本次更新版本中,PostgreSQLQueryBuilder类实现了DisposableBean接口,重写了destroy()方法调用closeConnections()方法。此外,SQLQueryExtenderTest 类使用 AnnotationConfigApplicationContext 的 registerShutdownHook() 方法注册一个关闭挂钩,以确保在关闭应用程序上下文时调用 closeConnections() 方法。
在您的情况下,您可以使用 @PreDestroy 注释来指定销毁 bean 时要调用的方法。 @PreDestroy 方法在 bean 从容器中移除之前被调用。在此方法中,您可以关闭 Connection 对象和您已打开的任何其他资源。
但是,您的情况似乎没有调用@PreDestroy 方法。这可能是因为您的 bean 没有被 Spring 容器正确管理。要解决此问题,您可以使用 @Component 注释确保您的 bean 被定义为 Spring bean。此外,您可以使用 @Scope 注释来指定 bean 的范围。在您的情况下,您可以使用 @Scope("prototype") 批注在每次从容器请求时创建一个新的 bean 实例。
这里是一个示例,说明如何修改代码以正确管理 Connection 对象:
@Component
@Scope("prototype")
public class PostgreSQLQueryBuilder implements SQLQueryBuilder {
private DatabaseMetaData metaData;
private Connection connection;
@Autowired
public PostgreSQLQueryBuilder(DataSource dataSource) {
try {
this.connection = dataSource.getConnection();
this.metaData = connection.getMetaData();
} catch (SQLException e) {
System.out.println("Connection exception");
e.printStackTrace();
}
}
@Override
public String queryForTable(String tableName){
String resultQuery = null;
List<String> columnList = new ArrayList<>();
if (getTables().contains(tableName)) {
try (ResultSet columns = metaData.getColumns(null, null, tableName, null)) {
//some logic
return resultQuery;
} catch (SQLException e) {
System.out.println("Exception");
e.printStackTrace();
}
}
return resultQuery;
}
@Override
public List<String> getTables() {
List<String> tables = new ArrayList<>();
try (ResultSet schemaRs = metaData.getSchemas()) {
while (schemaRs.next()) {
String currentSchema = schemaRs.getString(1);
try (ResultSet tableRS = metaData.getTables(null, currentSchema, "%", new String[]{"TABLE", "SYSTEM TABLE"})) {
while (tableRS.next()) {
tables.add(tableRS.getString("TABLE_NAME"));
}
}
}
} catch (SQLException e) {
System.out.println("Exception");
e.printStackTrace();
}
return tables;
}
@PreDestroy
private void closeConnections(){
try{
this.connection.close();
System.out.println("Connection closed");
}catch(Exception e){
//some logic
}
}
}
有了这些变化,当你获得一个新的 PostgreSQLQueryBuilder bean 实例时,Spring 将注入 DataSource 对象,创建一个新的 Connection 对象,并将它存储在连接字段中。当bean被销毁时,会调用@PreDestroy方法,关闭Connection对象