Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
public <T> T streamQuery(String sql, Function<Stream<SqlRowSet>, ? extends T> streamer, Object... args) {
return jdbcTemplate.query(sql, resultSet -> {
final SqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
final boolean parallel = false;
// The ResultSet API has a slight impedance mismatch with Iterators, so this conditional
// simply returns an empty iterator if there are no results
if (! {
return streamer.apply(, parallel));
Spliterator<SqlRowSet> spliterator = Spliterators.spliteratorUnknownSize(new Iterator<SqlRowSet>() {
private boolean first = true;
public boolean hasNext() {
return !rowSet.isLast();
public SqlRowSet next() {
if (!first || ! {
throw new NoSuchElementException();
first = false; // iterators can be unwieldy sometimes
return rowSet;
}, Spliterator.IMMUTABLE);
return streamer.apply(, parallel));
}, args);
Copy link

fun queryStream(sql: String, converter: (SqlRowSet) -> T, args: Array): Stream {
val rowSet = jdbcTemplate.queryForRowSet(sql, *args);

Meh, that's totally missing the point!
The idea is lazy consumption (on the fly row processing), but with JdbcTemplate::queryForRowSet you're just aggregating results in memory! For that to workJdbcTemplate would have to keep the db connection open after making that call and it just doesn't do that.
You can replace your whole snippet with jdbcTemplate.queryForList(...).stream() to get the same result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment