To make best use of the java8 stream and Spring4, I use the Stream API as follows on a JDBC resultSet that comes from Springs jsdbRestTemplate (code shortened and simplified):
public <T> T consumeResultStream(
String query,
Function<Stream<String>, T> extractorFunction
) {
return jdbcTemplate.query(
query,
resultSet -> {
Spliterator<String> spliterator =
Spliterators.spliteratorUnknownSize(
new Iterator<String>() {
@Override public boolean hasNext() {
return !resultSet.isAfterLast();
}
@Override public String next() {
String result = resultSet.getString(0);
resultSet.next();
return result;
}
},
Spliterator.IMMUTABLE);
resultStream = StreamSupport.stream(
spliterator, /*parallel*/ false);
}
return extractorFunction.apply(resultStream);
});
}
This seems to work fine. Clients can use the stream Api like this without worrying about jdbc classes
List<T> myResult = consumeResultStream("SELECT ...", stream ->
stream.filter((String s) -> ...)
.map(String s -> toT(s))
.collect(toList()));
However, when I refactor (trying to provide the stream to client methods instead), like this:
final Stream<String> stream =
jdbcTemplate.query(query, resultSet -> {
// ... same as above
return resultStream;
});
return extractorFunction.apply(stream);
I get
org.springframework.jdbc.InvalidResultSetAccessException:
The object is already closed [90007-199]
So it seems the data can only be read within the jdbcTemplate.query()
method. Is there a clean way I can circumvent this and return a lazy stream of the elements coming from the DB? Assume materializing the result and streaming that is not an option because of the size of the results (though pagination might be a better pattern).