使用 JdbcTemplate 流式传输 JDBC 查询结果

问题描述 投票:0回答:2

我将 Spring 与 Java 结合使用。

我需要从数据库查询中返回

Stream
对象(我使用
ObjectMapper
将它们映射到 JSON)。

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

我已经用

JpaRepository
做到了。

我想知道如何用

JdbcTemplate
来完成,这样做是否有好处?

即……我们可以使用

JdbcTemplate
甚至其他库来优化吞吐量和内存使用吗?

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

java spring jdbc spring-jdbc
2个回答
8
投票

是的,流会有一个优势,因为它是处理数据的常见抽象,而无需将所有数据都存储在内存中。例如。将流传递给 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);

6
投票

看看query()

的所有
JdbcTemplate
方法。

返回类型为

void
的那些显然不会在内存中构建完整的数据集。他们实际上都采取了
RowCallbackHandler
:

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

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

当然假设 JDBC 驱动程序不会将整个

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

© www.soinside.com 2019 - 2024. All rights reserved.