具有一对多关系的jooq单个查询
jooq single query with one to many relationship
我有一个 table 实验和一个 table 标签。一个实验可能有很多标签。
架构:
-------- --------
|Table1| 1 n |Table2|
| | <--------------> | |
| | | |
-------- --------
(experiment) (tags)
是否可以使用 jooq 创建一个查询,其中 returns 实验和相应的标签列表?
类似 Result<Record>
的内容,其中 Record 是一个 experimentRecord 和一个标签列表,或者 map<experimentRecord
、List<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) {}
您现在可以使用 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
我有一个 table 实验和一个 table 标签。一个实验可能有很多标签。 架构:
-------- --------
|Table1| 1 n |Table2|
| | <--------------> | |
| | | |
-------- --------
(experiment) (tags)
是否可以使用 jooq 创建一个查询,其中 returns 实验和相应的标签列表?
类似 Result<Record>
的内容,其中 Record 是一个 experimentRecord 和一个标签列表,或者 map<experimentRecord
、List<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 嵌套对多关系的方法时发现这个问题。
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) {}
您现在可以使用 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