Java JDBC+Spring 连接关闭

问题描述 投票:0回答:2

我有一个简单的类,可以搜索 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
}
java spring postgresql jdbc connection
2个回答
1
投票

当不再需要 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() 方法。


0
投票

在您的情况下,您可以使用 @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对象

© www.soinside.com 2019 - 2024. All rights reserved.