Using MULTISET
for nested collections with jOOQ 3.15
Starting from jOOQ 3.15, you can use the standard SQL MULTISET
operator to nest collections, and to abstract over the below SQL/XML or SQL/JSON serialisation format. Your query would look like this:
List<Location> locations
ctx.select(
LOCATION.NAME,
LOCATION.PLAYER,
multiset(
select(LOCATION2PLAYER.PLAYER_ID)
.from(LOCATION2PLAYER)
.where(LOCATION2PLAYER.LOCATION_ID.eq(LOCATION.LOCATION_ID))
).as("invitedPlayers")
)
.from(LOCATION)
.fetchInto(Location.class);
If your DTOs were immutable (e.g. Java 16 records), you can even avoid using reflection for mapping, and map type safely into your DTO constructors using constructor references and the new jOOQ 3.15 ad-hoc conversion feature.
List<Location> locations
ctx.select(
LOCATION.NAME,
LOCATION.PLAYER,
multiset(
select(LOCATION2PLAYER.PLAYER_ID)
.from(LOCATION2PLAYER)
.where(LOCATION2PLAYER.LOCATION_ID.eq(LOCATION.LOCATION_ID))
).as("invitedPlayers").convertFrom(r -> r.map(Record1::value1))
)
.from(LOCATION)
.fetch(Records.mapping(Location::new));
See also this blog post for more details about MULTISET
Using SQL/XML or SQL/JSON for nested collections with jOOQ 3.14
Starting from jOOQ 3.14, it's possible to nest collections using SQL/XML or SQL/JSON, if your RDBMS supports that. You can then use Jackson, Gson, or JAXB to map from the text format back to your Java classes. For example:
List<Location> locations
ctx.select(
LOCATION.NAME,
LOCATION.PLAYER,
field(
select(jsonArrayAgg(LOCATION2PLAYER.PLAYER_ID))
.from(LOCATION2PLAYER)
.where(LOCATION2PLAYER.LOCATION_ID.eq(LOCATION.LOCATION_ID))
).as("invitedPlayers")
.convertFrom(r -> r.map(Records.mapping(Pla)
)
.from(LOCATION)
.fetch(Records.mapping(Location::new));
In some database products, like PostgreSQL, you could even use SQL array types using ARRAY_AGG()
and skip using the intermediate XML or JSON format.
Note that JSON_ARRAYAGG()
aggregates empty sets into NULL
, not into an empty []
. If that's a problem, use COALESCE()
Historic answer (pre jOOQ 3.14)
jOOQ doesn't do this kind of POJO mapping out of the box yet, but you can leverage something like ModelMapper which features a dedicated jOOQ integration, which works for these scenarios to a certain extent.
Essentially, ModelMapper hooks into jOOQ's RecordMapper
API. More details here: