在我们看来,任何Iterable<T> ,都应该提供一个<R> collect(Collector<T, ?, R>) 方法,以允许使用标准的JDK收集器、来自jOOλ的收集器或你自己的收集器将内容转换为其他东西。 [org.jooq.lambda.Agg](https://www.jooq.org/products/jOO%CE%BB/javadoc/latest/org/jooq/lambda/Agg.html)或你自己的。
当使用jOOQ时,你不必等待JDK最终将这些有用的实用程序添加到Iterable API中。jOOQ的ResultQuery<R> 已经实现了Iterable<R> ,并在此基础上提供了额外的便利,如collect() 。
例如,使用Java 16的记录类型。
record Book (int id, String title) {}
List<Book> books =
ctx.select(BOOK.ID, BOOK.TITLE)
.from(BOOK)
.collect(Collectors.mapping(
r -> r.into(Book.class),
Collectors.toList()
));
还有其他的方法来映射事物,但为什么不使用Collector 。Collector 类型最好的一点是,它们的组成、类型安全和任意性,几乎就像Stream 管道。
我最近在Stack Overflow上发现了一个非常有趣的用例。那里的问题是,fetchGroups() 是相当简单的,而且没有左键连接的意识,这意味着当一个AUTHOR (父)没有BOOK (子),而不是一个空的列表,会有一个只有一个NULL 项目的列表。
Map<AuthorRecord, List<BookRecord>> result =
ctx.select()
.from(AUTHOR)
.leftJoin(BOOK).onKey()
.fetchGroups(AUTHOR, BOOK);
上面的方法对内联接很有效,但对左联接却没有意义。当然,我们应该在jOOQ中解决这个问题(https://github.com/jOOQ/jOOQ/issues/11888),但是使用Collectors ,你今天已经可以解决这个问题了。
只需写
Map<AuthorRecord, List<BookRecord>> result =
ctx.select()
.from(AUTHOR)
.leftJoin(BOOK).onKey()
.collect(groupingBy(
r -> r.into(AUTHOR),
filtering(
r -> r.get(BOOK.ID) != null,
mapping(
r -> r.into(BOOK),
toList()
)
)
));
// All assuming the usual static imports:
import static org.jooq.impl.DSL.*;
import static com.example.generated.Tables.*;
import static java.util.stream.Collectors.*;
一步一步来。
- 通过
AUTHOR,将结果分组,将键映射到AuthorRecord,就像jOOQ的那样fetchGroups() - 对于每个
AUTHOR,过滤掉那些BOOK,其BOOK.ID是null的记录。鉴于BOOK.ID是主键,它可能是null的唯一原因是左连接 - 将值映射到
BookRecord,就像jOOQ的那样fetchGroups() - 将子记录收集到一个列表中。
然后你就完成了。就像你在foreach循环中把ResultQuery 作为Iterable ,collect() 调用自动执行你的查询,管理所有资源,绕过中间的Result 数据结构,这里不需要。