But JDBC is still the old-school one.
Let's wrap it!
package org.example.jdbc.stream; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; public final class StreamHelper { public static class Record { private final Map<String, Object> fields = new HashMap<>(16); private final long count; private Record(final ResultSet resultSet) throws SQLException { final ResultSetMetaData metaData = resultSet.getMetaData(); count = metaData.getColumnCount(); for (int i = 1; i <= count; ++i) { fields.put(metaData.getColumnName(i), resultSet.getObject(i)); } } /** * Is there a column named like this? * * @param columnName is the column name in the query. * @return True if found. */ public boolean contains(final String columnName) { return fields.containsKey(columnName); } /** * Number of columns. * * @return Numer of columns. */ public long count() { return count; } /** * Get value casted to the requested type. * <p> * No type checking happens inside. It is your job to know the datatype in the database. * <p> * Example:<br> * {@code record.get("COLUMN1", Long.class); // returns a Long} * * @param columnName is the column name in the query. * @param type is Java type of the column. * @return The value casted to the Java type. */ public <T> T get(final String columnName, final Class<T> type) { return type.cast(getObject(columnName)); } /** * Get columns in the record. * * @return Collection of the column names. */ public Set<String> getColumns() { return Collections.unmodifiableSet(fields.keySet()); } /** * Get value as an object. * * @param columnName is the column name in the query. * @return The value. */ public Object getObject(final String columnName) { return fields.get(columnName); } /** * Get value as string. * * @param columnName is the column name in the query. * @return Value as string. */ public String getString(final String columnName) { return Objects.toString(fields.get(columnName)); } /** * Is the given cell null? * * @param columnName is the column name in the query. * @return True if null. */ public boolean isNull(final String columnName) { return getObject(columnName) == null; } @Override public String toString() { return fields.entrySet().stream().map(e -> e.getKey() + ": " + e.getValue()) .collect(Collectors.joining(", ")); } } /** * Wrap a ResultSet in a Stream. * <p> * The wrapper consumes the result set. The caller must close the result set after the stream * processing was finished. * * @param resultSet is the open result set to streamline. * @return A stream of rows. */ public static Stream<Record> asStream(final ResultSet resultSet) { // "est = Long.MAX_VALUE if infinite, unknown, or too expensive to compute." return StreamSupport.stream(new Spliterators.AbstractSpliterator<Record>(Long.MAX_VALUE, Spliterator.NONNULL | Spliterator.IMMUTABLE) { @Override public boolean tryAdvance(final Consumer<? super Record> action) { try { if (!resultSet.next()) { return false; } } catch (@SuppressWarnings("unused") final SQLException e) { return false; } try { action.accept(new Record(resultSet)); } catch (@SuppressWarnings("unused") final SQLException e) { return false; } return true; } }, true).parallel(); } private StreamHelper() { /* Hidden. */ } }Usage example:
import org.example.jdbc.stream.StreamHelper; import org.example.jdbc.stream.StreamHelper.Record; // ... @GET @Path("test") public Response test() { try (Connection connection = Database.connect(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT * FROM MY_TABLE")) { return Response.ok(StreamHelper.asStream(resultSet).map(Record::toString) .collect(Collectors.joining(", "))).build(); } catch (final SQLException | NamingException e) { return Response.serverError().entity(e.toString()).build(); } }