Java 1.8 comes with the Streams API, which is a great feature for writing readable code. I recently needed to stream over a JDBC ResultSet, which requires a custom Spliterator.
According to the Java documentation a Spliterator is:
An object for traversing and partitioning elements of a source. The source of elements covered by a Spliterator could be, for example, an array, a Collection, an IO channel, or a generator function.
ResultSetSpliterator
The easiest way of implementing a custom Spliterator is to derive from an AbstractSpliterator, which already implements
most of the Spliterator interface. The intent of the ResultSetSpliterator<TEntity>
iterator is to iterate over a given
ResultSet and map the current result to an Entity with a SqlMapper.
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
package de.bytefish.sqlmapper.iterator;
import de.bytefish.sqlmapper.SqlMapper;
import de.bytefish.sqlmapper.result.SqlMappingResult;
import java.sql.ResultSet;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
public class ResultSetSpliterator<TEntity> extends Spliterators.AbstractSpliterator<SqlMappingResult<TEntity>> {
private final ResultSet resultSet;
private final SqlMapper<TEntity> sqlMapper;
public ResultSetSpliterator(final SqlMapper<TEntity> sqlMapper, final ResultSet resultSet) {
super(Long.MAX_VALUE,Spliterator.ORDERED);
this.sqlMapper = sqlMapper;
this.resultSet = resultSet;
}
@Override
public boolean tryAdvance(Consumer<? super SqlMappingResult<TEntity>> action) {
if (next()) {
action.accept(sqlMapper.toEntity(resultSet));
return true;
} else {
return false;
}
}
private boolean next() {
try {
return resultSet.next();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Using the ResultSetSpliterator
The StreamSupport class provides low-level utility methods for creating and manipulating streams. The StreamSupport.stream
method makes it possible to create a new sequential or parallel Stream from a Spliterator.
In the example I am also creating the SqlMapper, which is not in the scope of this article, but it gives you a hint how to create the stream.
@Test
public void testToEntityStream() throws Exception {
// Number of persons to insert:
int numPersons = 10000;
// Insert the given number of persons:
insert(numPersons);
// Get all row of the Table:
ResultSet resultSet = selectAll();
// Create a SqlMapper, which maps between a ResultSet row and a Person entity:
SqlMapper<Person> sqlMapper = new SqlMapper<>(() -> new Person(), new PersonMap());
// Create the Stream using the StreamSupport class:
Stream<SqlMappingResult<Person>> stream = StreamSupport.stream(new ResultSetSpliterator<>(sqlMapper, resultSet), false);
// Collect the Results as a List:
List<SqlMappingResult<Person>> result = stream.collect(Collectors.toList());
// Assert the results:
Assert.assertEquals(numPersons, result.size());
}