可用版本:Dev (3.21) | 最新 (3.20) | 3.19 | 3.18 | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11

隐式路径JOIN

适用于 ✅ 开源版   ✅ 专业版   ✅ 企业版

在 SQL 中,很多 显式 JOIN 子句仅仅是为了从给定的子表检索父表的列。例如,我们会写

-- Get all books, their authors, and their respective language
SELECT
  a.first_name,
  a.last_name,
  b.title,
  l.cd AS language
FROM book b
JOIN author a ON b.author_id = a.id
JOIN language l ON b.language_id = l.id;

-- Count the number of books by author and language
SELECT
  a.first_name,
  a.last_name,
  l.cd AS language,
  COUNT(*)
FROM book
JOIN author a ON b.author_id = a.id
JOIN language l ON b.language_id = l.id
GROUP BY a.id, a.first_name, a.last_name, l.cd
ORDER BY a.first_name, a.last_name, l.cd

需要相当多的语法仪式(或者我们甚至可以称之为“噪音”)才能完成一个相对简单的工作。一个更简单的表示法是使用隐式连接

-- Get all books, their authors, and their respective language
SELECT
  b.author.first_name,
  b.author.last_name,
  b.title,
  b.language.cd AS language
FROM book b;

-- Count the number of books by author and language
SELECT
  b.author.first_name,
  b.author.last_name,
  b.language.cd AS language,
  COUNT(*)
FROM book b
GROUP BY
  b.author_id,
  b.author.first_name,
  b.author.last_name,
  b.language.cd
ORDER BY
  b.author.first_name,
  b.author.last_name,
  b.language.cd

请注意,这种替代表示法(取决于您的喜好)可能看起来更整洁和直接,因为访问表的父表(或实体的父实体)的语义是直截了当的。

从 jOOQ 3.11 开始,此语法支持一对一关系导航,从 jOOQ 3.19 开始也支持多对一关系导航。代码生成器在生成的表上生成相关的导航方法,这些方法可以以类型安全的方式使用。导航方法名称是

  • 如果子表和父表之间只有一个外键,则为父表名称(或子表名称,分别为)
  • 如果子表和父表之间有多个外键,则为外键名称

可以通过使用 代码生成器策略来覆盖此默认行为。

先前查询的 jOOQ 版本如下所示

// Get all books, their authors, and their respective language
create.select(
          BOOK.author().FIRST_NAME,
          BOOK.author().LAST_NAME,
          BOOK.TITLE,
          BOOK.language().CD.as("language"))
      .from(BOOK)
      .fetch();

// Count the number of books by author and language
create.select(
          BOOK.author().FIRST_NAME,
          BOOK.author().LAST_NAME,
          BOOK.language().CD.as("language"),
          count())
      .from(BOOK)
      .groupBy(
          BOOK.AUTHOR_ID,
          BOOK.author().FIRST_NAME,
          BOOK.author().LAST_NAME,
          BOOK.language().CD)
      .orderBy(
          BOOK.author().FIRST_NAME,
          BOOK.author().LAST_NAME,
          BOOK.language().CD)
      .fetch();

生成的 SQL 与原始 SQL 几乎相同 - 这种语法没有任何性能损失。

默认 JOIN 类型

生成的连接的默认类型是

  • 对于具有非空父对象的 to-one 路径段,为 INNER JOIN
  • 对于具有可空父对象的 to-one 路径段,为 LEFT JOIN
  • 对于隐式 to-many 路径段的例外,这些路径段尚未在 FROM 子句中显式声明,否则为 LEFT JOIN(另请参阅 隐式多对路径连接了解详细信息)

可以使用 Settings.renderImplicitJoinTypeSettings.renderImplicitJoinToManyType 来分别覆盖这些默认值,或者通过指定 显式路径连接

它是如何工作的

在 SQL 生成阶段,隐式连接路径被路径最后一个表的生成别名替换。这些路径被转换为一个连接图,该图始终 LEFT JOIN 到路径的“根表”。如果两个路径共享一个公共前缀,那么该前缀也在连接图中共享。

已知限制

  • 隐式 JOIN 目前只能用于访问列,不能用于生成连接。也就是说,不可能编写像 FROM book IMPLICIT JOIN book.author 这样的内容
  • 出于性能原因,隐式 JOIN 在整个 SQL 语句可用后添加到 SQL 字符串。这意味着,VisitListener SPI 实现无法观察到隐式连接的表

反馈

您对此页面有任何反馈吗? 我们很乐意倾听!

The jOOQ Logo