3
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);
        }
    }
}

在上面的代码中,ResultSet 被打开executeQuery但被关闭mapResultToRecord。那是关闭它的理想场所还是应该由该findRecords功能承担责任?

4

2 回答 2

2

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

我认为关闭 ResultSet 更有意义findRecords 。必须理解,executeQuery返回必须关闭的资源。没有办法解决这个问题。既然是findRecords调用executeQuery,我认为让它负责关闭这样做回来的 ResultSet 会更干净,这符合您的问题背后的想法。

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

public List<Record> findRecords(String name) {
    String query = prepareQuery(name);
    ResultSet resultSet = executeQuery(query);
    List<Record> result = mapResultToRecord(resultSet);
    resultSet.close();
    return result;
}

以另一种方式看待它(你拥有它的方式)。 findRecords的目的是准备和执行查询,然后将其交给mapResultsToRecord继续处理。 mapResultsToRecord的目的是根据在别处进行的查询结果创建记录列表,然后处理查询结果。

第一个故事读起来不是比第二个好很多吗?第一个问题不是比第二个问题更好地分离和定义问题吗?只是我不起眼的 0.02 美元。

为了进一步强调我的观点,我想说,如果你要让它保持原样,你会想要重命名mapResultsToRecordmapResultsToRecordAndCloseResultSet.

更新:@JohnKugelman 的好主意提供了另一个原因,即findRecords关闭 ResultSet 的地方。只有在这里才有意义使用 try-with-resource 块来获得这种替代方案:

public List<Record> findRecords(String name) throws SQLException {
    String query = prepareQuery(name);
    try (ResultSet resultSet = executeQuery(query)) {
        return mapResultToRecord(resultSet);
    }
}

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

于 2020-11-11T05:39:27.087 回答
1

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

将映射器作为回调传递给 executeQuery 方法将是一种改进。这就是 Spring-jdbc 所做的,它使用此方法定义了一个接口RowMapper

T mapRow(ResultSet rs, int rowNum) throws SQLException

它被传递到 JdbcTemplate 的查询方法中。映射器仅负责使用 ResultSet 行来填充对象,并且不参与关闭任何内容。如果映射器的职责仅限于映射单行,并且我们可以传入行号,那么同一个映射器可以用于查找单个行和获取多行。

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

findRecords 方法应该执行查询并调用映射器以从关闭 resultSet 的 try-block 中获取结果,可能像这样:

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;
}

没有详细说明 session.call 的作用。将此代码也提供 PreparedStatement 可能会很好,这样它就可以确保它被关闭。

于 2020-11-12T14:56:27.710 回答