Java 关闭资源是否应该始终由打开它的功能负责? public ResultSet executeQuery(字符串查询){ 返回会话。调用(查询) } 公共列表findRecords(字符串名称){ 字符串查询=prepareQuery(名称); 返回mapResultToRecord(executeQuery)); } 公共列表MapResultRecord(结果集rs){ 试一试{ 列表=新的ArrayList(); while(rs.hasNext()){ 添加(新记录(rs.next()); } }捕获(例外e){ logger.warn(“在调用集上迭代时出现异常”,e); }最后{ 试一试{ rs.close(); }捕获(例外情况除外){ logger.warn(“关闭结果集时出现异常”,例如); } } }

Java 关闭资源是否应该始终由打开它的功能负责? public ResultSet executeQuery(字符串查询){ 返回会话。调用(查询) } 公共列表findRecords(字符串名称){ 字符串查询=prepareQuery(名称); 返回mapResultToRecord(executeQuery)); } 公共列表MapResultRecord(结果集rs){ 试一试{ 列表=新的ArrayList(); while(rs.hasNext()){ 添加(新记录(rs.next()); } }捕获(例外e){ logger.warn(“在调用集上迭代时出现异常”,e); }最后{ 试一试{ rs.close(); }捕获(例外情况除外){ logger.warn(“关闭结果集时出现异常”,例如); } } },java,autocloseable,Java,Autocloseable,在上述代码中,结果集是由executeQuery打开的,但正在由mapResultToRecord关闭。这是关闭它的理想场所,还是应该由findRecords功能来承担责任?我的简短回答是“如果可能,可以” 我认为在findRecords中关闭结果集更有意义。必须理解,executeQuery返回一个必须关闭的资源。这是没有办法的。由于调用executeQuery的是findRecords,因此我认为让它负责关闭从中返回的结果集更为简洁,符合您问题背后的想法 一般规则是让每个函数尽可能有一个单一

在上述代码中,结果集是由
executeQuery
打开的,但正在由
mapResultToRecord
关闭。这是关闭它的理想场所,还是应该由
findRecords
功能来承担责任?

我的简短回答是“如果可能,可以”

我认为在
findRecords
中关闭结果集更有意义。必须理解,
executeQuery
返回一个必须关闭的资源。这是没有办法的。由于调用
executeQuery
的是
findRecords
,因此我认为让它负责关闭从中返回的结果集更为简洁,符合您问题背后的想法

一般规则是让每个函数尽可能有一个单一的目的
findRecords
的目的是利用查询来检索和处理一些数据
mapResultToRecord
的目的是通过从ResultSet中提取记录来创建记录列表。所有这些都读得非常好:

public ResultSet executeQuery(String query) {
    return session.call(query)
}


public List<Record> findRecords(String name) {
    String query = prepareQuery(name);
    return mapResultToRecord(executeQuery(query));
}

public List<Record> mapResultToRecord(ResultSet rs) {
    try {
        List<Record> list = new ArrayList<>();
        while(rs.hasNext()) {
            list.add(new Record(rs.next()));
        }
    } catch (Exception e) {
        logger.warn("exception while iterating over the recall set", e);
    } finally {
        try {
            rs.close();
        } catch (Exception ex) {
            logger.warn("exception while closing the result set", ex);
        }
    }
}

即使
mapResultToRecord
引发异常,此版本也保证关闭结果集。注意方法签名上的
抛出SQLException
。这是必要的,因为即使您没有看到
.close()
调用,它也隐式地存在,并且它可以引发SQLException。

如果您打开它,请关闭它。当负责关闭资源的代码与负责打开资源的代码不同时,很难确保在所有情况下都关闭资源。如果相同的代码打开和关闭,它可以使用try-with-resources确保关闭

对于executeQuery方法来说,将映射器作为回调传入是一种改进。Spring jdbc就是这样做的,它使用以下方法定义了一个接口:

public List<Record> findRecords(String name) throws SQLException {
    String query = prepareQuery(name);
    try (ResultSet resultSet = executeQuery(query)) {
        return mapResultToRecord(resultSet);
    }
}
它被传递到JdbcTemplate的查询方法中。映射程序只负责使用ResultSet行填充对象,不涉及关闭任何内容。如果映射器的职责仅限于映射一行,并且我们可以传入行号,那么同一映射器可以用于查找单个行和获取多行

同时,查询执行代码变得非常通用,查询和映射器是特定于特定查询的唯一部分,它们都是从外部传入的

findRecords方法应执行查询并调用映射器,以从关闭resultSet的try块中获取结果,可能如下所示:

T mapRow(ResultSet rs, int rowNum) throws SQLException
List findRows(字符串queryName,行映射器映射器)抛出SQLException{
列表=新的ArrayList();
字符串sql=prepareQuery(queryName);
try(ResultSet ResultSet=session.call(sql);){
for(int i=1;resultSet.hasNext();i+=1){
添加(mapper.mapRow(resultSet,i));
}
}
退货清单;
}

关于session.call的作用没有给出任何细节。如果给这段代码一个PreparedStatement,这样它就可以确保它被关闭,这可能会很好。

这无论如何都不会起作用。您正在泄漏
连接
,或者正在关闭连接,从而关闭
结果集
,或者您正在尝试使用持久连接,这很容易失败:您应该使用本地
连接
、本地
PreparedStatement
和本地
结果集
,并将它们全部关闭,并使用连接池支持
连接。
T mapRow(ResultSet rs, int rowNum) throws SQLException
List<T> findRows(String queryName, RowMapper<T> mapper) throws SQLException {
    List<T> list = new ArrayList<>();
    String sql = prepareQuery(queryName);
    try (ResultSet resultSet = session.call(sql);) {
        for (int i = 1; resultSet.hasNext(); i += 1) {
            list.add(mapper.mapRow(resultSet, i));
        }
    }
    return list;
}