具有一对多关系的jooq单个查询

jooq single query with one to many relationship

我有一个 table 实验和一个 table 标签。一个实验可能有很多标签。 架构:

--------                  --------
|Table1|  1           n   |Table2|
|      | <--------------> |      |
|      |                  |      |
--------                  --------
(experiment)              (tags)

是否可以使用 jooq 创建一个查询,其中 returns 实验和相应的标签列表?

类似 Result<Record> 的内容,其中 Record 是一个 experimentRecord 和一个标签列表,或者 map<experimentRecordList<TagRecord>.

我也有一个查询returns只有一个结果,有什么方便的吗?

编辑:java8,最新的 jooq。

有很多方法可以使用 SQL 和/或 jOOQ 实现嵌套集合。我只是浏览其中的一些:

使用连接

如果您不深入嵌套这些集合,使用 JOIN 对结果进行非规范化(扁平化)可能会为您解决问题,而不会因为数据被复制而增加太多开销。本质上,你会写:

Map<ExperimentRecord, Result<Record>> map =
DSL.using(configuration)
   .select()
   .from(EXPERIMENT)
   .join(TAGS)
   .on(...)
   .fetchGroups(EXPERIMENT);

以上地图包含作为键的实验记录,以及包含作为值的所有标签的嵌套集合。

创建两个查询

如果您想具体化一个复杂的对象图,使用连接可能不再是最佳选择。相反,您可能希望从两个不同的查询中收集客户端中的数据:

Result<ExperimentRecord> experiments = 
DSL.using(configuration)
   .selectFrom(EXPERIMENT)
   .fetch();

Result<TagsRecord> tags =
DSL.using(configuration)
   .selectFrom(TAGS)
   .where(... restrict to the previous experiments ...)
   .fetch();
 

现在,将两个结果合并到客户的记忆中,例如

experiments.stream()
           .map(e -> new ExperimentWithTags(
                e, 
                tags.stream()
                    .filter(t -> e.getId().equals(t.getExperimentId()))
                    .collect(Collectors.toList())
           ));

使用 SQL/XML 或 SQL/JSON

嵌套集合

这个问题不需要它,但其他人可能会在寻找一种使用 jOOQ 嵌套对多关系的方法时发现这个问题。 。从 jOOQ 3.14 开始,您可以使用 RDBMS 的 SQL/XML 或 SQL/JSON 功能,然后使用 Jackson、Gson 或 JAXB 嵌套集合,如下所示:

List<Experiment> experiments =
ctx.select(
      EXPERIMENT.asterisk(),
      field(
        select(jsonArrayAgg(jsonObject(TAGS.fields())))
        .from(TAGS)
        .where(TAGS.EXPERIMENT_ID.eq(EXPERIMENT.ID))
      ).as("tags")
    )
   .from(EXPERIMENT)
   .fetchInto(Experiment.class);

其中 Experiment 是自定义 Java class 像这样:

class Experiment {
  long id;
  String name;
  List<Tag> tags;
}

class Tag {
  long id;
  String name;
}

使用 MULTISET

嵌套集合

比上面的还要好,you can hide using SQL/XML or SQL/JSON behind jOOQ 3.15's new MULTISET operator support。假设上面的 Java classes 是 Java 16 条记录(或任何其他不可变的 classes),您甚至可以将嵌套集合类型安全地映射到您的 DTO:

List<Experiment> experiments =
ctx.select(
      EXPERIMENT.ID,
      EXPERIMENT.NAME,
      multiset(
        select(TAGS.ID, TAGS.NAME)
        .from(TAGS)
        .where(TAGS.EXPERIMENT_ID.eq(EXPERIMENT.ID))
      ).as("tags").convertFrom(r -> r.map(Records.mapping(Tag::new)))
    )
   .from(EXPERIMENT)
   .fetch(Records.mapping(Experiment::new));

其中 Experiment 是自定义 Java class 像这样:

record Experiment(long id, String name, List<Tag> tags) {}
record Tag(long id, String name) {}

See also this blog post for more information.

您现在可以使用 SimpleFlatMapper 将您的结果映射到 Tuple2<ExperimentRecord, List<TagRecord>>。您只需要做的就是。

1 - 创建一个映射器,指定键列,假设它是 id

JdbcMapper mapper = 
    JdbcMapperFactory
     .newInstance()
     .addKeys(EXPERIMENT.ID.getName())
     .newMapper(new TypeReference<Tuple2<ExperimentRecord, List<TagRecord>>>() {});

2 - 在查询的结果集中使用映射器

try (ResultSet rs = DSL.using(configuration)
   .select()
   .from(EXPERIMENT)
   .join(TAGS)
   .on(...)
   .fetchResultSet()) {
    Stream<Tuple2<ExperimentRecord, List<TagRecord>>> stream = mapper.stream(rs);
    ....
}

有关详细信息,请参阅 here