/*
* 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.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Stream;
import org.jdbi.v3.core.collector.ElementTypeNotFoundException;
import org.jdbi.v3.core.collector.NoSuchCollectorException;
import org.jdbi.v3.core.config.Configurable;
import org.jdbi.v3.core.generic.GenericType;
import org.jdbi.v3.core.mapper.ColumnMapper;
import org.jdbi.v3.core.mapper.GenericMapMapperFactory;
import org.jdbi.v3.core.mapper.MapMapper;
import org.jdbi.v3.core.mapper.NoSuchMapperException;
import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.mapper.RowViewMapper;
import org.jdbi.v3.core.mapper.SingleColumnMapper;
import org.jdbi.v3.core.mapper.reflect.BeanMapper;
import org.jdbi.v3.core.qualifier.QualifiedType;
import org.jdbi.v3.core.result.internal.RowViewImpl;
import org.jdbi.v3.core.statement.StatementContext;
import org.jdbi.v3.meta.Beta;
Provides access to the contents of a ResultSet
by mapping to Java types. /**
* Provides access to the contents of a {@link ResultSet} by mapping to Java types.
*/
public interface ResultBearing {
Returns a ResultBearing backed by the given result set supplier and context.
Params: - resultSetSupplier – result set supplier
- ctx – the statement context
Returns: a ResultBearing
/**
* Returns a ResultBearing backed by the given result set supplier and context.
*
* @param resultSetSupplier result set supplier
* @param ctx the statement context
* @return a ResultBearing
*/
static ResultBearing of(Supplier<ResultSet> resultSetSupplier, StatementContext ctx) {
return new ResultBearing() {
@Override
public <R> R scanResultSet(ResultSetScanner<R> mapper) {
try {
return mapper.scanResultSet(resultSetSupplier, ctx);
} catch (SQLException e) {
throw new ResultSetException("Error reading result set", e, ctx);
}
}
};
}
Invokes the mapper with a result set supplier, and returns the value returned by the mapper.
Params: - mapper – result set scanner
Type parameters: - <R> – result type returned by the mapper.
Returns: the value returned by the mapper.
/**
* Invokes the mapper with a result set supplier, and returns the value returned by the mapper.
* @param mapper result set scanner
* @param <R> result type returned by the mapper.
* @return the value returned by the mapper.
*/
<R> R scanResultSet(ResultSetScanner<R> mapper);
Maps this result set to a ResultIterable
of the given element type. Params: - type – the type to map the result set rows to
Type parameters: - <T> – the type to map the result set rows to
See Also: Returns: a ResultIterable
of the given type.
/**
* Maps this result set to a {@link ResultIterable} of the given element type.
*
* @param type the type to map the result set rows to
* @param <T> the type to map the result set rows to
* @return a {@link ResultIterable} of the given type.
* @see Configurable#registerRowMapper(RowMapper)
* @see Configurable#registerRowMapper(org.jdbi.v3.core.mapper.RowMapperFactory)
* @see Configurable#registerColumnMapper(org.jdbi.v3.core.mapper.ColumnMapperFactory)
* @see Configurable#registerColumnMapper(ColumnMapper)
*/
default <T> ResultIterable<T> mapTo(Class<T> type) {
return mapTo(QualifiedType.of(type));
}
Maps this result set to a ResultIterable
of the given element type. Params: - type – the type to map the result set rows to
Type parameters: - <T> – the type to map the result set rows to
See Also: Returns: a ResultIterable
of the given type.
/**
* Maps this result set to a {@link ResultIterable} of the given element type.
*
* @param type the type to map the result set rows to
* @param <T> the type to map the result set rows to
* @return a {@link ResultIterable} of the given type.
* @see Configurable#registerRowMapper(RowMapper)
* @see Configurable#registerRowMapper(org.jdbi.v3.core.mapper.RowMapperFactory)
* @see Configurable#registerColumnMapper(org.jdbi.v3.core.mapper.ColumnMapperFactory)
* @see Configurable#registerColumnMapper(ColumnMapper)
*/
default <T> ResultIterable<T> mapTo(GenericType<T> type) {
return mapTo(QualifiedType.of(type));
}
Maps this result set to a ResultIterable
of the given element type. Params: - type – the type to map the result set rows to
See Also: Returns: a ResultIterable
of the given type.
/**
* Maps this result set to a {@link ResultIterable} of the given element type.
*
* @param type the type to map the result set rows to
* @return a {@link ResultIterable} of the given type.
* @see Configurable#registerRowMapper(RowMapper)
* @see Configurable#registerRowMapper(org.jdbi.v3.core.mapper.RowMapperFactory)
* @see Configurable#registerColumnMapper(org.jdbi.v3.core.mapper.ColumnMapperFactory)
* @see Configurable#registerColumnMapper(ColumnMapper)
*/
default ResultIterable<?> mapTo(Type type) {
return mapTo(QualifiedType.of(type));
}
Maps this result set to a ResultIterable
of the given qualified element type. Params: - type – the qualified type to map the result set rows to
See Also: Returns: a ResultIterable
of the given type.
/**
* Maps this result set to a {@link ResultIterable} of the given qualified element type.
*
* @param type the qualified type to map the result set rows to
* @return a {@link ResultIterable} of the given type.
* @see Configurable#registerRowMapper(RowMapper)
* @see Configurable#registerRowMapper(org.jdbi.v3.core.mapper.RowMapperFactory)
* @see Configurable#registerColumnMapper(org.jdbi.v3.core.mapper.ColumnMapperFactory)
* @see Configurable#registerColumnMapper(ColumnMapper)
*/
@Beta
default <T> ResultIterable<T> mapTo(QualifiedType<T> type) {
return scanResultSet((supplier, ctx) -> {
RowMapper<T> mapper = ctx.findMapperFor(type)
.orElseThrow(() -> new NoSuchMapperException("No mapper registered for type " + type));
return ResultIterable.of(supplier, mapper, ctx);
});
}
Maps this result set to a ResultIterable
of the given element type, using BeanMapper
. Params: - type – the bean type to map the result set rows to
Type parameters: - <T> – the bean type to map the result set rows to
Returns: a ResultIterable
of the given type.
/**
* Maps this result set to a {@link ResultIterable} of the given element type, using {@link BeanMapper}.
*
* @param type the bean type to map the result set rows to
* @param <T> the bean type to map the result set rows to
* @return a {@link ResultIterable} of the given type.
*/
@SuppressWarnings("deprecation")
default <T> ResultIterable<T> mapToBean(Class<T> type) {
return map(BeanMapper.of(type));
}
Maps this result set to a ResultIterable
of Map<String,Object>
. Keys are column names, and values are column values. Returns: a ResultIterable<Map<String,Object>>
.
/**
* Maps this result set to a {@link ResultIterable} of {@code Map<String,Object>}. Keys are column names, and
* values are column values.
*
* @return a {@link ResultIterable ResultIterable<Map<String,Object>>}.
*/
default ResultIterable<Map<String, Object>> mapToMap() {
return map(new MapMapper());
}
Params: - valueType – the class to map the resultset columns to
Type parameters: - <T> – the value type
See Also: Returns: a Map
of String and the given type.
/**
* Maps this result set to a {@link Map} of {@link String} and the given value class.
*
* @param <T> the value type
* @param valueType the class to map the resultset columns to
* @return a {@link Map} of String and the given type.
* @see Configurable#registerColumnMapper(ColumnMapper)
*/
@Beta
default <T> ResultIterable<Map<String, T>> mapToMap(Class<T> valueType) {
return scanResultSet((supplier, ctx) -> ResultIterable.of(supplier, GenericMapMapperFactory.getMapperForValueType(valueType, ctx.getConfig()), ctx));
}
Params: - valueType – the type to map the resultset columns to
Type parameters: - <T> – the value type
See Also: Returns: a Map
of String and the given type.
/**
* Maps this result set to a {@link Map} of {@link String} and the given value type.
*
* @param <T> the value type
* @param valueType the type to map the resultset columns to
* @return a {@link Map} of String and the given type.
* @see Configurable#registerColumnMapper(ColumnMapper)
*/
@Beta
default <T> ResultIterable<Map<String, T>> mapToMap(GenericType<T> valueType) {
return scanResultSet((supplier, ctx) -> ResultIterable.of(supplier, GenericMapMapperFactory.getMapperForValueType(valueType, ctx.getConfig()), ctx));
}
Maps this result set to a ResultIterable
, using the given column mapper. Params: - mapper – column mapper used to map the first column of each row
Type parameters: - <T> – the type to map the result set rows to
Returns: a ResultIterable
of type <T>
.
/**
* Maps this result set to a {@link ResultIterable}, using the given column mapper.
*
* @param mapper column mapper used to map the first column of each row
* @param <T> the type to map the result set rows to
* @return a {@link ResultIterable} of type {@code <T>}.
*/
default <T> ResultIterable<T> map(ColumnMapper<T> mapper) {
return map(new SingleColumnMapper<>(mapper));
}
Maps this result set to a ResultIterable
, using the given row mapper. Params: - mapper – mapper used to map each row
Type parameters: - <T> – the type to map the result set rows to
Returns: a ResultIterable
of type <T>
.
/**
* Maps this result set to a {@link ResultIterable}, using the given row mapper.
*
* @param mapper mapper used to map each row
* @param <T> the type to map the result set rows to
* @return a {@link ResultIterable} of type {@code <T>}.
*/
default <T> ResultIterable<T> map(RowMapper<T> mapper) {
return scanResultSet((supplier, ctx) -> ResultIterable.of(supplier, mapper, ctx));
}
Maps this result set to a ResultIterable
, using the given RowViewMapper
. This overload only exists to allow RowViewMapper as the type of a lambda expression. Params: - mapper – RowViewMapper used to map each row
Type parameters: - <T> – the type to map the result set rows to
Returns: a ResultIterable
of type <T>
.
/**
* Maps this result set to a {@link ResultIterable}, using the given {@link RowViewMapper}.
* This overload only exists to allow RowViewMapper as the type of a lambda expression.
*
* @param mapper RowViewMapper used to map each row
* @param <T> the type to map the result set rows to
* @return a {@link ResultIterable} of type {@code <T>}.
*/
default <T> ResultIterable<T> map(RowViewMapper<T> mapper) {
return map((RowMapper<T>) mapper);
}
Reduce the result rows using the given row reducer.
Params: - reducer – the row reducer.
Type parameters: See Also: Returns: the stream of result elements
/**
* Reduce the result rows using the given row reducer.
*
* @param reducer the row reducer.
* @param <C> Mutable result container type
* @param <R> Result element type
* @return the stream of result elements
* @see RowReducer
*/
default <C, R> Stream<R> reduceRows(RowReducer<C, R> reducer) {
return scanResultSet((supplier, ctx) -> {
try (ResultSet rs = supplier.get()) {
RowView rowView = new RowViewImpl(rs, ctx);
C container = reducer.container();
while (rs.next()) {
reducer.accumulate(container, rowView);
}
return reducer.stream(container);
} catch (SQLException e) {
throw new UnableToProduceResultException(e, ctx);
} finally {
ctx.close();
}
});
}
Reduce the result rows using a Map<K, V>
as the result container. Params: - accumulator – accumulator function which gathers data from each
RowView
into the result map.
Type parameters: Returns: the stream of elements in the container's Map.values()
collection, in the order they were inserted.
/**
* Reduce the result rows using a {@link Map Map<K, V>} as the
* result container.
*
* @param accumulator accumulator function which gathers data from each
* {@link RowView} into the result map.
* @param <K> map key type
* @param <V> map value type
* @return the stream of elements in the container's {@link Map#values()}
* collection, in the order they were inserted.
*/
default <K, V> Stream<V> reduceRows(BiConsumer<Map<K, V>, RowView> accumulator) {
return reduceRows((LinkedHashMapRowReducer<K, V>) accumulator::accept);
}
Reduce the results. Using a BiFunction<U, RowView, U>
, repeatedly combine query results until only a single value remains. Params: - seed – the
U
to combine with the first result - accumulator – the function to apply repeatedly
Type parameters: - <U> – the type of the accumulator
Returns: the final U
/**
* Reduce the results. Using a {@code BiFunction<U, RowView, U>}, repeatedly
* combine query results until only a single value remains.
*
* @param <U> the type of the accumulator
* @param seed the {@code U} to combine with the first result
* @param accumulator the function to apply repeatedly
* @return the final {@code U}
*/
default <U> U reduceRows(U seed, BiFunction<U, RowView, U> accumulator) {
return scanResultSet((supplier, ctx) -> {
try (ResultSet rs = supplier.get()) {
RowView rv = new RowViewImpl(rs, ctx);
U result = seed;
while (rs.next()) {
result = accumulator.apply(result, rv);
}
return result;
} catch (SQLException e) {
throw new UnableToProduceResultException(e, ctx);
} finally {
ctx.close();
}
});
}
Reduce the results. Using a ResultSetAccumulator
, repeatedly combine query results until only a single value remains. Params: - seed – 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 ResultSetAccumulator}, repeatedly
* combine query results until only a single value remains.
*
* @param <U> the accumulator type
* @param seed the {@code U} to combine with the first result
* @param accumulator the function to apply repeatedly
* @return the final {@code U}
*/
default <U> U reduceResultSet(U seed, ResultSetAccumulator<U> accumulator) {
return scanResultSet((supplier, ctx) -> {
try (ResultSet rs = supplier.get()) {
U result = seed;
while (rs.next()) {
result = accumulator.apply(result, rs, ctx);
}
return result;
} catch (SQLException e) {
throw new UnableToProduceResultException(e, ctx);
} finally {
ctx.close();
}
});
}
Collect the results using the given collector. Do not attempt to accumulate the RowView
objects into the result--they are only valid within the Collector.accumulator()
function. Instead, extract mapped types from the RowView by calling RowView.getRow()
or RowView.getColumn()
. Params: - collector – the collector to collect the result rows.
Type parameters: Returns: the result of the collection
/**
* Collect the results using the given collector. Do not attempt to accumulate the
* {@link RowView} objects into the result--they are only valid within the
* {@link Collector#accumulator()} function. Instead, extract mapped types from the
* RowView by calling {@code RowView.getRow()} or {@code RowView.getColumn()}.
*
* @param collector the collector to collect the result rows.
* @param <A> the mutable accumulator type used by the collector.
* @param <R> the result type returned by the collector.
* @return the result of the collection
*/
default <A, R> R collectRows(Collector<RowView, A, R> collector) {
return scanResultSet((supplier, ctx) -> {
try (ResultSet rs = supplier.get()) {
RowView rv = new RowViewImpl(rs, ctx);
A acc = collector.supplier().get();
BiConsumer<A, RowView> consumer = collector.accumulator();
while (rs.next()) {
consumer.accept(acc, rv);
}
return collector.finisher().apply(acc);
} catch (SQLException e) {
throw new UnableToProduceResultException(e, ctx);
} finally {
ctx.close();
}
});
}
Collect the results into a container of the given type. A collector
must be registered for the container type, which knows the element type
for the container. A mapper must be registered for the element type.
This method is equivalent to ResultBearing.mapTo(elementType).collect(containerCollector)
.
Params: - containerType – the container type into which results will be collected
Type parameters: - <R> – the result container type
Returns: a container into which result rows have been collected
/**
* Collect the results into a container of the given type. A collector
* must be registered for the container type, which knows the element type
* for the container. A mapper must be registered for the element type.
* <p>
* This method is equivalent to {@code ResultBearing.mapTo(elementType).collect(containerCollector)}.
* </p>
* @param containerType the container type into which results will be collected
* @param <R> the result container type
* @return a container into which result rows have been collected
*/
@SuppressWarnings("unchecked")
default <R> R collectInto(Class<R> containerType) {
return (R) collectInto((Type) containerType);
}
Collect the results into a container of the given generic type. A collector
must be registered for the container type, which knows the element type
for the container. A mapper must be registered for the element type.
This method is equivalent to ResultBearing.mapTo(elementType).collect(containerCollector)
.
Example:
Map<Long, User> usersById = handle.createQuery("select * from user")
.configure(MapEntryMappers.class, cfg -> cfg.setKeyColumn("id"))
.collectInto(new GenericType<Map<Long, User>>() {});
Params: - containerType – the container type into which results will be collected
Type parameters: - <R> – the result container type
Returns: a container into which result rows have been collected
/**
* Collect the results into a container of the given generic type. A collector
* must be registered for the container type, which knows the element type
* for the container. A mapper must be registered for the element type.
* <p>
* This method is equivalent to {@code ResultBearing.mapTo(elementType).collect(containerCollector)}.
* </p>
* <p>
* Example:
* </p>
* <pre>
* Map<Long, User> usersById = handle.createQuery("select * from user")
* .configure(MapEntryMappers.class, cfg -> cfg.setKeyColumn("id"))
* .collectInto(new GenericType<Map<Long, User>>() {});
* </pre>
*
* @param containerType the container type into which results will be collected
* @param <R> the result container type
* @return a container into which result rows have been collected
*/
@SuppressWarnings("unchecked")
default <R> R collectInto(GenericType<R> containerType) {
return (R) collectInto(containerType.getType());
}
Collect the results into a container of the given type. A collector
must be registered for the container type, which knows the element type
for the container. A mapper must be registered for the element type.
This method is equivalent to ResultBearing.mapTo(elementType).collect(containerCollector)
.
Params: - containerType – the container type into which results will be collected
Returns: a container into which result rows have been collected
/**
* Collect the results into a container of the given type. A collector
* must be registered for the container type, which knows the element type
* for the container. A mapper must be registered for the element type.
* <p>
* This method is equivalent to {@code ResultBearing.mapTo(elementType).collect(containerCollector)}.
* </p>
*
* @param containerType the container type into which results will be collected
* @return a container into which result rows have been collected
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
default Object collectInto(Type containerType) {
return scanResultSet((rs, ctx) -> {
Collector collector = ctx.findCollectorFor(containerType)
.orElseThrow(() -> new NoSuchCollectorException("No collector registered for container type " + containerType));
Type elementType = ctx.findElementTypeFor(containerType)
.orElseThrow(() -> new ElementTypeNotFoundException("Unknown element type for container type " + containerType));
RowMapper<?> mapper = ctx.findMapperFor(elementType)
.orElseThrow(() -> new NoSuchMapperException("No mapper registered for element type " + elementType));
return ResultIterable.of(rs, mapper, ctx).collect(collector);
});
}
}