我将 Spring 与 Java 结合使用。
我需要从数据库查询中返回
Stream
对象(我使用 ObjectMapper
将它们映射到 JSON)。
查询结果可能非常大(超过 500k 个对象),所以我不想将它们存储在内存中。
我已经用
JpaRepository
做到了。
我想知道如何用
JdbcTemplate
来完成,这样做是否有好处?
即……我们可以使用
JdbcTemplate
甚至其他库来优化吞吐量和内存使用吗?
我的目标实际上是在最后找到运行查询并将所有对象在内存/时间/处理方面打印到输出流的最佳方法。
是的,流会有一个优势,因为它是处理数据的常见抽象,而无需将所有数据都存储在内存中。例如。将流传递给 HTTP 响应。
如果你使用Spring 5.3,有一个方便的方法
JdbcTemplate.queryForStream()
可以这样使用:
String sql = "select * from table";
Stream<Person > stream = jdbcTemplate.queryForStream(sql, (resultSet, rowNum) -> {
return new Person(resultSet.getInt(1), resultSet.getString(2));
});
旧版本的
JDBCTemplate
没有直接用于流的功能。但是您可以使用底层数据库连接来创建流:
String sql = "select * from table";
Connection connection = jdbcTemplate.getDataSource().getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery();
PersonMapper personMapper = new PersonMapper();
Spliterator<Person> spliterator =
Spliterators.spliteratorUnknownSize(
new Iterator<Person>() {
@Override public boolean hasNext() {
try {
return !resultSet.isAfterLast();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override public Person next() {
try {
if (resultSet.isBeforeFirst()) {
resultSet.next();
}
Person result = new Person(resultSet.getInt(1), resultSet.getString(2));
resultSet.next();
return result;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
},
Spliterator.IMMUTABLE);
Runnable closer = () -> {
try {
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
};
Stream<Person> = StreamSupport.stream(spliterator, false).onClose(closer);
看看query()
JdbcTemplate
方法。
返回类型为
void
的那些显然不会在内存中构建完整的数据集。他们实际上都采取了RowCallbackHandler
:
使用的接口,用于在JdbcTemplate
每行的基础上处理ResultSet
的行。
processRow()
回调方法可以将数据添加到流式 JSON 文本中,确保最小的内存使用。
当然假设 JDBC 驱动程序不会将整个
ResultSet
加载到内存中,但这是一个不同的问题,因此解决方案(如果需要)完全取决于所使用的 JDBC 驱动程序。