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
职能部门承担责任?
我的简短回答是"如果可能,"是";。
我认为在findRecords
中关闭ResultSet更有意义。必须理解,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美元。
为了进一步强调我的观点,我想说,如果你要保持原样,你会想把mapResultsToRecord
重命名为mapResultsToRecordAndCloseResultSet
。
更新:@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。
如果你打开它,就关闭它。每当负责关闭资源的代码与负责打开资源的代码不同时,很难确保在所有情况下都关闭资源。如果同一个代码打开和关闭,它可以使用try-with-resources来确保关闭。
executeQuery方法将映射程序作为回调传入将是一种改进。这就是Springjdbc所做的,它用以下方法定义了一个接口RowMapper:
T mapRow(ResultSet rs, int rowNum) throws SQLException
它被传递到JdbcTemplate的查询方法中。映射器只负责使用ResultSet行来填充对象,而不参与关闭任何内容。如果映射器的职责仅限于映射一行,并且我们可以传入行号,那么同一个映射器可以用于查找单个行和获取多行。
同时,查询执行代码变得非常通用,查询和映射器是特定于特定查询的唯一部分,它们都是从外部传入的。
findRecords方法应该执行查询并调用映射程序,以从关闭resultSet的try块中获取结果,可能如下所示:
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可能会很好,这样它就可以确保它被关闭。