After having read this article, I wish to use Spring to stream database query results directly to a JSON response to ensure constant-memory usage (no greedy loading of a List
in memory).
Similar to what is done in the article with Hibernate, I assembled a greetingRepository
object which returns a stream of the database contents based on a JdbcTemplate
. In that implementation, I create an iterator over the queried ResultSet
, and I return the stream as follows:
return StreamSupport.stream(spliterator(), false).onClose(() -> {
log.info("Closing ResultSetIterator stream");
JdbcUtils.closeResultSet(resultSet);
});
i.e. with an onClose()
method guaranteeing that the underlying ResultSet
will be closed if the stream is declared in a try-with-resources
construct:
try(Stream<Greeting> stream = greetingRepository.stream()) {
// operate on the stream
} // ResultSet underlying the stream will be guaranteed to be closed
But as in the article, I want this stream to be consumed by a custom object mapper (the enhanced MappingJackson2HttpMessageConverter
defined in the article). If we take the try-with-resources
need aside, this is feasible as follows:
@RequestMapping(method = GET)
Stream<GreetingResource> stream() {
return greetingRepository.stream().map(GreetingResource::new);
}
However as a colleague commented at the bottom of that article, this does not take care of closing underlying resources.
In the context of Spring MVC, how can I stream from the database all the way into a JSON response and still guarantee that the ResultSet
will be closed? Could you provide a concrete example solution?
You could create a construct to defer the query execution at the serialization time. This construct will start and end the transaction programmaticaly.
Using a dedicated json serializer:
Which could be used like this:
All the work will be done when jackson will serialize the object. It may be or not an issue regarding the error handling (eg. using controller advice).