使用ResultQuery.collect()来实现强大的映射功能

在我们看来,任何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()
   ));

还有其他的方法来映射事物,但为什么不使用CollectorCollector 类型最好的一点是,它们的组成、类型安全和任意性,几乎就像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.*;

一步一步来:

  1. 通过AUTHOR ,将结果分组,将键映射到AuthorRecord ,就像jOOQ的那样fetchGroups()
  2. 对于每个AUTHOR ,过滤掉那些BOOK ,其BOOK.IDnull 的记录。鉴于BOOK.ID 是主键,它可能是null 的唯一原因是左连接
  3. 将值映射到BookRecord ,就像jOOQ的那样fetchGroups()
  4. 将子记录收集到一个列表中。

然后你就完成了,就像你在foreach循环中把ResultQuery 作为Iterablecollect() 调用自动执行你的查询,管理所有资源,绕过中间的Result 数据结构,这里不需要。

猜你喜欢

转载自juejin.im/post/7126371082546151437