【问题标题】:Stream result of JDBC query with JDBCTemplate使用 JDBCTemplate 的 JDBC 查询结果流
【发布时间】:2021-02-20 15:33:56
【问题描述】:

我正在使用spring java。

我需要从数据库查询中返回 Stream 的对象(我使用 ObjectMapper 将它们映射到 JSON)。

查询结果可能非常大(超过 500k 个对象),所以我不想将它们存储在内存中。

我已经用JpaRepository 完成了。

我想知道如何使用JdbcTemplate 完成它,这样做是否有优势?

即...我们能否使用JdbcTemplate 甚至其他库来优化吞吐量和内存使用情况。

我的最终目标是找到运行查询并将所有对象打印到内存/时间/处理方面的输出流的最佳方式。

【问题讨论】:

  • 升级到 Spring 5.3 并使用新添加的 queryForStream 方法。或者如果您打算写信给OutputStream,请使用RowCallbackHandler

标签: java spring jdbc


【解决方案1】:

是的,流将有一个优势,因为它是一种处理数据的通用抽象,而无需将所有数据都放在内存中。例如。将流传递给 HTTP 响应。

春季 5.3

如果你使用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));
});

以前的 Spring 版本

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

【讨论】:

    【解决方案2】:

    查看所有query() methods of JdbcTemplate

    返回类型为void 的那些显然不会在内存中建立完整的数据集。他们实际上都采取了RowCallbackHandler

    JdbcTemplate 使用的接口,用于以每行为基础处理ResultSet 的行。

    然后,processRow() 回调方法可以将数据添加到流式 JSON 文本中,从而确保最少的内存使用。

    这当然假设 JDBC 驱动程序不会将整个 ResultSet 加载到内存中,但这是一个不同的问题,因此解决方案(如果需要)完全取决于使用的 JDBC 驱动程序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-08
      • 1970-01-01
      • 2019-07-07
      • 1970-01-01
      • 2023-03-11
      • 2017-10-11
      • 1970-01-01
      相关资源
      最近更新 更多