英文:
Jooq query nested object with different name than table
问题
我有一个与 https://stackoverflow.com/questions/71090417/jooq-query-nested-object 中描述的问题非常相似的问题。总结一下,我有这个 POJO:
public class CourseComposite {
private String courseName;
private Teacher teacher;
private List<Student> students;
// ...
}
我正在使用 Jooq 的 DefaultRecordMapper
来将记录映射到这个 POJO,查询如下:
ctx().select(
COURSE.NAME.as("courseName"),
TEACHERS,
multiset(selectFrom(STUDENT).where(STUDENT.ID_COURSE.eq(COURSE.ID))).as("students")
)
.from(COURSE).innerJoin(TEACHERS).on(COURSE.ID.eq(TEACHER.ID_COURSE))
.where(...)
.fetchInto(CourseComposite.class);
然而,教师(teacher)未被映射(teacher 字段为 null)。我认为这是因为我的 PostgreSQL 表名为 teachers
而不是 teacher
(这是与上面提到的 Stack Overflow 问题唯一的不同)。
我尝试写如下内容:
ctx().select(COURSE.NAME.as("courseName"), TEACHERS.as("teacher"), multiset(...))
然而,在这种情况下,我得到了一个异常:
缺少表 "teacher" 的 FROM 子句条目
是否有办法告诉 Jooq 将 TEACHERS
绑定到 Teacher teacher
对象?
英文:
I have a problem really similar to the one described https://stackoverflow.com/questions/71090417/jooq-query-nested-object. To summarize I have this pojo:
public class CourseComposite {
private String courseName;
private Teacher teacher;
private List<Student> students;
...
}
and I'm using the Jooq DefaultRecordMapper
to map the record to the pojo, having the following query:
ctx().select(
COURSE.NAME.as("courseName"),
TEACHERS,
multiset(selectFrom(STUDENT).where(STUDENT.ID_COURSE.eq(COURSE.ID))).as("students")
)
.from(COURSE).innerJoin(TEACHERS).on(COURSE.ID.eq(TEACHER.ID_COURSE))
.where(...)
.fetchInto(CourseComposite.class);
However teacher is not mapped (the teacher field is null). I think it's because my postgresql table is called teachers
and not teacher
(this is the only difference with the stack overflow question mentioned above).
I tried to write:
ctx().select(COURSE.NAME.as("courseName"), TEACHERS.as("teacher"), multiset(...))
however in this case I get an exception:
> missing FROM-clause entry for table "teacher"
Is there a way to tell jooq that TEACHERS
should be binded to the Teacher teacher
object?
答案1
得分: 1
这是“预期的”,尽管可以承认有点奇怪,因为您实际上是为Table
而不是SelectField
创建别名(API相同)。您仅在SELECT
子句中为特定目的而别名化TEACHERS.as("teacher")
的事实对于jOOQ来说是未知的。您可以像对待任何其他表别名一样使用这个别名。这样考虑一下:
Teachers teacher = TEACHERS.as("teacher");
现在,您可以按如下方式使用它:
ctx().select(..., teacher, ...)
jOOQ不能假定您只想在SELECT
子句中应用这个别名。如果根据PostgreSQL的错误消息提示,您还将其添加到FROM
子句中呢?例如:
innerJoin(teacher)
因为在那种情况下,将teacher
别名引用投射出来才有意义,而不是TEACHERS
表引用。您可能会认为jOOQ可以检测出什么是正确的事情,因为您明显没有在FROM
子句中包含teacher
,但是可能会出现许多特殊情况,尤其是当您同时拥有两者(通过自连接)时。
因此,解决方案是在FROM
子句中使用teacher
,而不是TEACHERS
。
替代方案
您可以通过将映射移入查询中来解决问题,至少在某种程度上,使用临时转换器。您可以编写:
TEACHERS.convertFrom(r -> r.into(Teacher.class))
在这种情况下,从TeachersRecord
到您的Teacher
类的映射将在尝试将嵌套记录映射到父对象之前发生。此外,值得考虑彻底删除基于反射的DefaultRecordMapper
,并将您的对象转换为记录:
public record CourseComposite(
String courseName,
Teacher teacher,
List<Student> students
) {}
现在,您可以在查询末尾使用fetchInto(Records.mapping(CourseComposite::new))
,一切都是类型安全的!
英文:
This is "expected", though admittedly, a bit weird, because you're really aliasing a Table
, not a SelectField
(the API is the same). The fact that you're aliasing TEACHERS.as("teacher")
only in the SELECT
clause for that particular purpose is not known to jOOQ. You might as well use the alias like any other table alias. Think about it this way:
Teachers teacher = TEACHERS.as("teacher");
Now, you'd be using it as follows:
ctx().select(..., teacher, ...)
jOOQ can't just assume you meant for the alias to apply only in the SELECT
clause. What if you added it to your FROM
clause as well, as indicated by PostgreSQL's error message? I.e.
innerJoin(teacher)
Because then, it would make sense to project the teacher
alias reference, not the TEACHERS
table reference. You might argue that jOOQ could detect what's The Right Thing™, because you clearly didn't include teacher
in the FROM
clause, but there can be many edge cases, especially when you have both (via self-join).
So, the solution is to just use teacher
in the FROM
clause, instead of TEACHERS
.
Alternatives
You can obviously work around the problem by moving the mapping into the query, at least in parts, using an ad-hoc converter. You could write:
TEACHERS.convertFrom(r -> r.into(Teacher.class))
In that case, the mapping from the TeachersRecord
to your Teacher
class would happen before trying to map the nested record into the parent object. It's also worth thinking about removing the reflection based DefaultRecordMapper
entirely, and turn your objects into records:
public record CourseComposite(
String courseName,
Teacher teacher,
List<Student> students
) {}
Now, you can use fetchInto(Records.mapping(CourseComposite::new))
at the end of your query, all type safe!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论