在我们看来,任何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
数据结构,这里不需要。