Class JoinRowReducer<R>
- Type Parameters:
R- the root type, and the result element type
- All Implemented Interfaces:
RowReducer<org.jdbi.v3.core.result.JoinRowReducer.Container<R>, R>
RowReducer that reduces the rows of a join query into a stream of root objects and links the
joined objects to their root. One type is one table: every object is identified by its type and its key
column, so a table row is one instance no matter through how many relations the query reaches it.
Columns are named relative to the relation, in the style of @Nested mappers. The root type reads
its columns without a prefix. A joined type reads them under the relation prefix and an underscore, and
nested relations compound: id, author_id, author_award_id. The key column is
id unless of(Class, String) names another. A relation is present on a path when the
result set has its key column there, so select the root's own foreign key columns under another label
than a relation key, or not at all: with SELECT b.* and a relation category, the book's
category_id column alone would produce a Category that has only its key.
Keys are compared by value: numeric keys are equal when their values are, whatever their JDBC types, so an
INT key matches a BIGINT or a NUMERIC key of the same value, and byte[] keys
are compared by content.
List<Book> books = handle.createQuery(
"SELECT b.id, b.title, a.id author_id, a.name author_name, e.id editor_id, e.name editor_name "
+ "FROM books b JOIN persons a ON a.id = b.author_id JOIN persons e ON e.id = b.editor_id")
.reduceRows(JoinRowReducer.of(Book.class).mappedBy(BeanMapper::of)
.one("author", Person.class, Book::setAuthor)
.one("editor", Person.class, Book::setEditor))
.toList();
one declares a to-one relation and many a to-many relation, both over inner and outer joins: a NULL key produces no object
for that row. A joined type can have relations of its own, declared on a nested reducer that is passed in
place of the class. A class that is declared anywhere in the reducer, the root type included, refers to that
declaration, so the author and the editor above are one Person instance when they are the same
person, and a self join is of(Category.class).one("parent", Category.class, Category::setParent)
with the parent being the same instance as the root category with that key. The recursion of a self join
ends where the selected columns end: no parent_parent_id column, no grandparent.
Mappers come from the registry by type and prefix, see RowView.getRow(Class, String): the root
takes the mapper registered for its type without a prefix, custom or reflective, and each relation path
that the query selects takes a mapper registered with its prefix. Or pass a mapper factory such as
BeanMapper::of to mappedBy(BiFunction) and register nothing. A factory on the root applies
to every type that has none of its own, and a factory is also the way to use a custom row mapper for a
joined type.
Linkers run once per distinct pair after the last row, and the relations of a joined type are linked
before the joined type is linked to its parent, so a linker receives a complete object unless the graph
is recursive. If the query selects a type on several paths, the object is mapped from the shallowest
path, and among paths of one depth from the one laid out first, breadth first in declaration order, so
the result does not depend on row order.
Root objects are returned in the order of their first row, the objects of a many relation are
linked to a parent in the order of their first row with it, and only keys that appear in the root key
column become roots. A reduction fails when the result set has no root key column, when a declared
relation has its key column on no path, when a root mapper returns null, or when a one relation
sees a second, different key for the same parent. The column checks run on the first row, so a result
set with no rows passes them.
Instances are immutable. Each call to one, many or mappedBy returns a new
reducer, and all mutable state lives in the container of one reduction, so a reducer can be shared between
statements and threads. The protected copy constructor gives a subclass the public no-arg constructor that
@UseRowReducer requires.
-
Method Summary
Modifier and TypeMethodDescriptionvoidaccumulate(org.jdbi.v3.core.result.JoinRowReducer.Container<R> container, RowView rowView) Accumulate data from the current row into the result container.org.jdbi.v3.core.result.JoinRowReducer.Container<R> Returns a new, empty result container.<J> JoinRowReducer<R> many(String prefix, Class<J> joinedType, BiConsumer<? super R, ? super J> linker) Adds a to-many relation to a type that is declared elsewhere in the reducer, or that has no relations.<J> JoinRowReducer<R> many(String prefix, JoinRowReducer<J> joined, BiConsumer<? super R, ? super J> linker) Adds a to-many relation to a type with relations of its own.mappedBy(BiFunction<Class<?>, String, ? extends RowMapper<?>> mapperFactory) Uses the given factory to create the mapper for a type and column prefix, for exampleBeanMapper::of, instead of the registered mapper.static <R> JoinRowReducer<R> Creates a reducer for a type whose key column isid.static <R> JoinRowReducer<R> Creates a reducer for a type with the given key column.<J> JoinRowReducer<R> one(String prefix, Class<J> joinedType, BiConsumer<? super R, ? super J> linker) Adds a to-one relation to a type that is declared elsewhere in the reducer, or that has no relations.<J> JoinRowReducer<R> one(String prefix, JoinRowReducer<J> joined, BiConsumer<? super R, ? super J> linker) Adds a to-one relation to a type with relations of its own.Returns a stream of result elements from the result container.
-
Method Details
-
of
Creates a reducer for a type whose key column isid.- Type Parameters:
R- the type- Parameters:
type- the type, one per table- Returns:
- a reducer that produces one object per distinct key
-
of
Creates a reducer for a type with the given key column.- Type Parameters:
R- the type- Parameters:
type- the type, one per tablekeyColumn- the column that identifies a row of the table, relative to the relation prefix- Returns:
- a reducer that produces one object per distinct key
-
mappedBy
public JoinRowReducer<R> mappedBy(BiFunction<Class<?>, String, ? extends RowMapper<?>> mapperFactory) Uses the given factory to create the mapper for a type and column prefix, for exampleBeanMapper::of, instead of the registered mapper. On the root reducer, the factory also applies to every type in the reducer that has no factory of its own.JoinRowReducer.of(Book.class) .mappedBy((type, prefix) -> type == Book.class ? bookMapper : BeanMapper.of(type, prefix))- Parameters:
mapperFactory- creates the mapper for a type and a prefix, the empty prefix for the root- Returns:
- a new reducer that maps with the factory
-
one
public <J> JoinRowReducer<R> one(String prefix, Class<J> joinedType, BiConsumer<? super R, ? super J> linker) Adds a to-one relation to a type that is declared elsewhere in the reducer, or that has no relations. A type that is declared nowhere gets the key columnid; passof(type, keyColumn)for another.- Type Parameters:
J- the joined type- Parameters:
prefix- the column prefix of the relation, for exampleauthorforauthor_idjoinedType- the joined typelinker- sets the joined object on the parent, for exampleBook::setAuthor- Returns:
- a new reducer that also links the joined object
-
one
public <J> JoinRowReducer<R> one(String prefix, JoinRowReducer<J> joined, BiConsumer<? super R, ? super J> linker) Adds a to-one relation to a type with relations of its own.- Type Parameters:
J- the joined type- Parameters:
prefix- the column prefix of the relationjoined- the reducer for the joined type, which declares its relationslinker- sets the joined object on the parent- Returns:
- a new reducer that also links the joined object
-
many
public <J> JoinRowReducer<R> many(String prefix, Class<J> joinedType, BiConsumer<? super R, ? super J> linker) Adds a to-many relation to a type that is declared elsewhere in the reducer, or that has no relations. A type that is declared nowhere gets the key columnid; passof(type, keyColumn)for another.- Type Parameters:
J- the joined type- Parameters:
prefix- the column prefix of the relation, for examplephoneforphone_idjoinedType- the joined typelinker- adds the joined object to the parent, for exampleContact::addPhone- Returns:
- a new reducer that also links the joined objects
-
many
public <J> JoinRowReducer<R> many(String prefix, JoinRowReducer<J> joined, BiConsumer<? super R, ? super J> linker) Adds a to-many relation to a type with relations of its own.- Type Parameters:
J- the joined type- Parameters:
prefix- the column prefix of the relationjoined- the reducer for the joined type, which declares its relationslinker- adds the joined object to the parent- Returns:
- a new reducer that also links the joined objects
-
container
Description copied from interface:RowReducerReturns a new, empty result container.- Specified by:
containerin interfaceRowReducer<org.jdbi.v3.core.result.JoinRowReducer.Container<R>, R>- Returns:
- a new result container.
-
accumulate
public void accumulate(org.jdbi.v3.core.result.JoinRowReducer.Container<R> container, RowView rowView) Description copied from interface:RowReducerAccumulate data from the current row into the result container. Do not attempt to accumulate theRowViewitself into the result container--it is only valid within theaccumulate()method invocation. Instead, extract mapped types from the RowView by callingRowView.getRow()orRowView.getColumn()and store those values in the container.- Specified by:
accumulatein interfaceRowReducer<org.jdbi.v3.core.result.JoinRowReducer.Container<R>, R>- Parameters:
container- the result containerrowView- row view over the current result set row.
-
stream
Description copied from interface:RowReducerReturns a stream of result elements from the result container.- Specified by:
streamin interfaceRowReducer<org.jdbi.v3.core.result.JoinRowReducer.Container<R>, R>- Parameters:
container- the result container- Returns:
- stream of result elements.
-