有很多方法可以使用SQL和/或jOOQ实现嵌套集合。我只是通过其中一些:
使用联接
如果您没有深入嵌套这些集合,则使用 a 对结果进行非规范化(平展化)可能会为您解决问题,而不会在重复数据时增加太多开销。从本质上讲,您将编写: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);
哪里是像这样的自定义 Java 类:Experiment
class Experiment {
long id;
String name;
List<Tag> tags;
}
class Tag {
long id;
String name;
}
嵌套集合使用MULTISET
比上述更好的是,您可以在jOOQ 3.15的新MULTISET
运算符支持后面隐藏使用SQL / XML或SQL / JSON。假设上述 Java 类是 Java 16 记录(或任何其他不可变类),您甚至可以将嵌套集合类型安全地映射到 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));
哪里是像这样的自定义 Java 类:Experiment
record Experiment(long id, String name, List<Tag> tags) {}
record Tag(long id, String name) {}
有关详细信息,另请参阅此博客文章。