Java JDBC+Spring Connection关闭

li9yvcax  于 2023-04-04  发布在  Java
关注(0)|答案(2)|浏览(286)

我有一个简单的类,可以搜索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
}
jv2fixgn

jv2fixgn1#

当不再需要JDBC连接等资源时,显式关闭它们是一个很好的做法。关闭资源有助于将它们释放回资源池,防止资源泄漏并节省系统资源。
在您的示例中,您可以在带有@PreDestroy注解的closeConnections()方法中关闭连接资源。该方法应在销毁bean时调用。
但是,您提到没有调用closeConnections()方法。这可能是由于在application context关闭时PostgreSQL QueryBuilder bean没有被销毁。要解决这个问题,您可以使用registerShutdownHook()方法向Spring ApplicationContext注册一个shutdown钩子。
下面是代码的更新版本,它在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));
    }
}

在此更新版本中,PostgreSQL QueryBuilder类实现了DisposableBean接口,并覆盖destroy()方法以调用closeConnections()方法。此外,SQLQueryExtenderTest类使用AnnotationConfigApplicationContext的registerShutdownHook()方法注册了一个关闭钩子,以确保在关闭应用程序上下文时调用closeConnections()方法。

nmpmafwu

nmpmafwu2#

在本例中,您可以使用@PreDestroy注解指定销毁bean时要调用的方法。在bean从容器中删除之前调用@PreDestroy方法。在此方法中,您可以关闭Connection对象和已打开的任何其他资源。
然而,在您的情况下似乎没有调用@PreDestroy方法。这可能是因为Spring容器没有正确管理您的bean。要解决这个问题,您可以通过使用@Component annotation来确保您的bean被定义为Spring bean。此外,您可以使用@Scope annotation来指定bean的范围。在您的情况下,可以使用@Scope(“prototype”)注解在每次从容器请求bean时创建一个新的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
        }
    }
}

通过这些更改,当您获得PostgreSQL QueryBuilder bean的新示例时,Spring将注入DataSource对象,创建新的Connection对象,并将其存储在connection字段中。当bean被销毁时,@PreDestroy方法将被调用,Connection对象将被关闭。

相关问题