Jooq 查询嵌套对象,其名称不同于表格。

huangapple go评论86阅读模式
英文:

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&lt;Student&gt; students;
  ...
}

and I'm using the Jooq DefaultRecordMapper to map the record to the pojo, having the following query:

    ctx().select(
            COURSE.NAME.as(&quot;courseName&quot;), 
            TEACHERS, 
            multiset(selectFrom(STUDENT).where(STUDENT.ID_COURSE.eq(COURSE.ID))).as(&quot;students&quot;)
          )
          .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(&quot;courseName&quot;), TEACHERS.as(&quot;teacher&quot;), 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(&quot;teacher&quot;) 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(&quot;teacher&quot;);

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&trade;, 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 -&gt; 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&lt;Student&gt; students
) {}

Now, you can use fetchInto(Records.mapping(CourseComposite::new)) at the end of your query, all type safe!

huangapple
  • 本文由 发表于 2023年7月3日 19:52:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76604478.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定