/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jdbi.v3.core.result;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.statement.StatementContext;

import static java.util.Spliterators.spliteratorUnknownSize;

An Iterable of values, usually mapped from a ResultSet. Generally, ResultIterables may only be traversed once.
Type parameters:
  • <T> – iterable element type
/** * An {@link Iterable} of values, usually mapped from a {@link java.sql.ResultSet}. Generally, ResultIterables may only * be traversed once. * * @param <T> iterable element type */
@FunctionalInterface public interface ResultIterable<T> extends Iterable<T> {
Returns a ResultIterable backed by the given result set supplier, mapper, and context.
Params:
  • supplier – result set supplier
  • mapper – row mapper
  • ctx – statement context
Type parameters:
  • <T> – the mapped type
Returns:the result iterable
/** * Returns a ResultIterable backed by the given result set supplier, mapper, and context. * * @param supplier result set supplier * @param mapper row mapper * @param ctx statement context * @param <T> the mapped type * @return the result iterable */
static <T> ResultIterable<T> of(Supplier<ResultSet> supplier, RowMapper<T> mapper, StatementContext ctx) { return () -> { try { return new ResultSetResultIterator<>(supplier.get(), mapper, ctx); } catch (SQLException e) { try { ctx.close(); } catch (Exception e1) { e.addSuppressed(e1); } throw new ResultSetException("Unable to iterator result set", e, ctx); } }; }
Returns a ResultIterable backed by the given iterator.
Params:
  • iterator – the result iterator
Type parameters:
  • <T> – iterator element type
Returns:a ResultIterable
/** * Returns a ResultIterable backed by the given iterator. * @param iterator the result iterator * @param <T> iterator element type * @return a ResultIterable */
static <T> ResultIterable<T> of(ResultIterator<T> iterator) { return () -> iterator; }
Stream all the rows of the result set out with an Iterator. The Iterator must be closed to release database resources.
Returns:the results as a streaming Iterator
/** * Stream all the rows of the result set out * with an {@code Iterator}. The {@code Iterator} must be * closed to release database resources. * @return the results as a streaming Iterator */
@Override ResultIterator<T> iterator(); @Override default void forEach(Consumer<? super T> action) { try (ResultIterator<T> iterator = iterator()) { iterator.forEachRemaining(action); } }
Returns the only row in the result set. Returns null if the row itself is null.
Throws:
Returns:the only row in the result set.
/** * Returns the only row in the result set. Returns {@code null} if the row itself is * {@code null}. * @throws IllegalStateException if the result set contains zero or multiple rows * @return the only row in the result set. */
default T one() { try (ResultIterator<T> iter = iterator()) { if (!iter.hasNext()) { throw new IllegalStateException("Expected one element, but found none"); } final T r = iter.next(); if (iter.hasNext()) { throw new IllegalStateException("Expected one element, but found multiple"); } return r; } }
Returns the only row in the result set, if any. Returns Optional.empty() if zero rows are returned, or if the row itself is null.
Throws:
Returns:the only row in the result set, if any.
/** * Returns the only row in the result set, if any. Returns {@code Optional.empty()} if zero * rows are returned, or if the row itself is {@code null}. * @throws IllegalStateException if the result set contains multiple rows * @return the only row in the result set, if any. */
default Optional<T> findOne() { try (ResultIterator<T> iter = iterator()) { if (!iter.hasNext()) { return Optional.empty(); } final T r = iter.next(); if (iter.hasNext()) { throw new IllegalStateException("Expected zero to one elements, but found multiple"); } return Optional.ofNullable(r); } }
Get the only row in the result set.
Throws:
  • IllegalStateException – if zero or multiple rows are returned
Returns:the object mapped from the singular row in the results
Deprecated:use one() or findOne() instead.
/** * Get the only row in the result set. * @throws IllegalStateException if zero or multiple rows are returned * @return the object mapped from the singular row in the results * @deprecated use {@link #one()} or {@link #findOne()} instead. */
@Deprecated default T findOnly() { return one(); }
Returns the first row in the result set. Returns null if the row itself is null.
Throws:
Returns:the first row in the result set.
/** * Returns the first row in the result set. Returns {@code null} if the row itself is * {@code null}. * @throws IllegalStateException if zero rows are returned * @return the first row in the result set. */
default T first() { try (ResultIterator<T> iter = iterator()) { if (!iter.hasNext()) { throw new IllegalStateException("Expected at least one element, but found none"); } return iter.next(); } }
Returns the first row in the result set, if present. Returns Optional.empty() if zero rows are returned or the first row is null.
Returns:the first row in the result set, if present.
/** * Returns the first row in the result set, if present. Returns {@code Optional.empty()} if * zero rows are returned or the first row is {@code null}. * @return the first row in the result set, if present. */
default Optional<T> findFirst() { try (ResultIterator<T> iter = iterator()) { return iter.hasNext() ? Optional.ofNullable(iter.next()) : Optional.empty(); } }
Returns the stream of results.

Note: the returned stream owns database resources, and must be closed via a call to BaseStream.close(), or by using the stream in a try-with-resources block:

try (Stream<T> stream = query.stream()) {
  // do stuff with stream
}
See Also:
Returns:the stream of results.
/** * Returns the stream of results. * * <p> * Note: the returned stream owns database resources, and must be closed via a call to {@link Stream#close()}, or * by using the stream in a try-with-resources block: * </p> * * <pre> * try (Stream&lt;T&gt; stream = query.stream()) { * // do stuff with stream * } * </pre> * * @return the stream of results. * * @see #useStream(StreamConsumer) * @see #withStream(StreamCallback) */
default Stream<T> stream() { ResultIterator<T> iterator = iterator(); return StreamSupport.stream(spliteratorUnknownSize(iterator, 0), false) .onClose(iterator::close); }
Passes the stream of results to the consumer. Database resources owned by the query are released before this method returns.
Params:
  • consumer – a consumer which receives the stream of results.
Type parameters:
  • <X> – the exception type thrown by the callback, if any
Throws:
  • X – any exception thrown by the callback
/** * Passes the stream of results to the consumer. Database resources owned by the query are * released before this method returns. * * @param consumer a consumer which receives the stream of results. * @param <X> the exception type thrown by the callback, if any * * @throws X any exception thrown by the callback */
default <X extends Exception> void useStream(StreamConsumer<T, X> consumer) throws X { withStream(stream -> { consumer.useStream(stream); return null; }); }
Passes the stream of results to the callback. Database resources owned by the query are released before this method returns.
Params:
  • callback – a callback which receives the stream of results, and returns some result.
Type parameters:
  • <R> – the type returned by the callback
  • <X> – the exception type thrown by the callback, if any
Throws:
  • X – any exception thrown by the callback
Returns:the value returned by the callback.
/** * Passes the stream of results to the callback. Database resources owned by the query are * released before this method returns. * * @param callback a callback which receives the stream of results, and returns some result. * @param <R> the type returned by the callback * @param <X> the exception type thrown by the callback, if any * * @return the value returned by the callback. * * @throws X any exception thrown by the callback */
default <R, X extends Exception> R withStream(StreamCallback<T, R, X> callback) throws X { try (Stream<T> stream = stream()) { return callback.withStream(stream); } }
Returns results in a List.
Returns:results in a List.
/** * Returns results in a {@link List}. * * @return results in a {@link List}. */
default List<T> list() { return collect(Collectors.toList()); }
Collect the results into a container specified by a collector.
Params:
  • collector – the collector
Type parameters:
  • <R> – the generic type of the container
Returns:the container with the query result
/** * Collect the results into a container specified by a collector. * * @param collector the collector * @param <R> the generic type of the container * @return the container with the query result */
default <R> R collect(Collector<T, ?, R> collector) { try (Stream<T> stream = stream()) { return stream.collect(collector); } }
Reduce the results. Using a BiFunction<U, T, U>, repeatedly combine query results until only a single value remains.
Params:
  • identity – the U to combine with the first result
  • accumulator – the function to apply repeatedly
Type parameters:
  • <U> – the accumulator type
Returns:the final U
/** * Reduce the results. Using a {@code BiFunction<U, T, U>}, repeatedly * combine query results until only a single value remains. * * @param <U> the accumulator type * @param identity the {@code U} to combine with the first result * @param accumulator the function to apply repeatedly * @return the final {@code U} */
default <U> U reduce(U identity, BiFunction<U, T, U> accumulator) { try (Stream<T> stream = stream()) { return stream.reduce(identity, accumulator, (u, v) -> { throw new UnsupportedOperationException("parallel operation not supported"); }); } } }