jooq 具有一对多关系的单个查询
Posted
技术标签:
【中文标题】jooq 具有一对多关系的单个查询【英文标题】:jooq single query with one to many relationship 【发布时间】:2016-02-24 00:52:07 【问题描述】:我有一个表格实验和一个表格标签。一个实验可能有多个标签。 架构:
-------- --------
|Table1| 1 n |Table2|
| | <--------------> | |
| | | |
-------- --------
(experiment) (tags)
是否可以使用 jooq 创建返回实验和相应标签列表的查询?
类似于Result<Record>
,其中Record 是一个experimentRecord 和一个标签列表,或者一个map<experimentRecord
、List<TagRecord>
。
我也有一个查询,它只返回一个结果,有什么方便的吗?
编辑:java8,最新的 jooq。
【问题讨论】:
哪个 Java 版本,哪个 JooQ 版本?另外,你的桌子是什么? 【参考方案1】:有很多方法可以使用 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 嵌套对多关系的方法时发现这个问题。 I've provided an answer here。从 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 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 类是 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));
Experiment
是一个自定义 Java 类,如下所示:
record Experiment(long id, String name, List<Tag> tags)
record Tag(long id, String name)
See also this blog post for more information.
【讨论】:
谢谢,我有时知道我只有一个结果,有没有捷径? @Leander:我不确定你的意思……什么结果?EXPERIMENT
?还是TAGS
?
@Leander:我明白了。您可以致电fetchOne()
或fetchOptional()
而不是fetch()
来获取个人记录
但是这样就没有把握掌握TAGS集合了吗?
@tfeiner:对不起,我不确定我是否理解你所说的“掌握标签集合”的意思......?【参考方案2】:
您现在可以使用SimpleFlatMapper 将您的结果映射到Tuple2<ExperimentRecord, List<TagRecord>>
。您需要做的就是。
1 - 创建一个映射器,指定键列,假设它是 id
JdbcMapper mapper =
JdbcMapperFactory
.newInstance()
.addKeys(EXPERIMENT.ID.getName())
.newMapper(new TypeReference<Tuple2<ExperimentRecord, List<TagRecord>>>() );
2 - 在查询的 ResultSet 上使用映射器
try (ResultSet rs = DSL.using(configuration)
.select()
.from(EXPERIMENT)
.join(TAGS)
.on(...)
.fetchResultSet())
Stream<Tuple2<ExperimentRecord, List<TagRecord>>> stream = mapper.stream(rs);
....
详情请见here
【讨论】:
以上是关于jooq 具有一对多关系的单个查询的主要内容,如果未能解决你的问题,请参考以下文章