jOOQ 3.17在DML中也支持隐式连接

112 阅读3分钟

从jOOQ 3.11开始,就支持隐式连接了。隐式连接是一个JOIN (主要是一个LEFT JOIN ),由于路径表达式的存在而隐式生成。如果SQL原生支持这种语法,它就会像这样:

SELECT
  cu.first_name,
  cu.last_name,
  cu.address.city.country.country
FROM customer AS cu

所有这些都是对一堆显式写的LEFT JOIN 表达式的方便:

SELECT
  cu.first_name,
  cu.last_name,
  co.country
FROM customer AS cu
LEFT JOIN address AS a USING (address_id)
LEFT JOIN city AS ci USING (city_id)
LEFT JOIN country AS co USING (country_id)

通过jOOQ,如果你使用代码生成,就可以使用这个功能

ctx.select(
      CUSTOMER.FIRST_NAME,
      CUSTOMER.LAST_NAME,
      CUSTOMER.address().city().country().COUNTRY_)
   .from(CUSTOMER)
   .fetch();

到目前为止,这个功能只在SELECT 语句中可用,而不是在UPDATE ,或DELETE

支持DML中的隐式连接

从jOOQ 3.17和#7508开始,强大的路径表达式现在也可以在DML语句中使用,比如UPDATEDELETE 。例如,让我们更新所有语言为英语的书籍。

在一个假设的SQL方言中,这可以写成如下:

UPDATE book
SET book.status = 'SOLD OUT'
WHERE book.language.cd = 'en';

DELETE book
WHERE book.language.cd = 'en';

或者,用jOOQ:

ctx.update(BOOK)
   .set(BOOK.STATUS, SOLD_OUT)
   .where(BOOK.language().CD.eq("en"))
   .execute();

ctx.delete(BOOK)
   .where(BOOK.language().CD.eq("en"))
   .execute();

使用to-one隐式连接路径表达式的语句的语义似乎很清楚。上述语句翻译成实际的SQL可能是这样的。

使用相关的子查询

这种模拟是直截了当的。

它也可以用于SELECT 查询中的隐式JOIN 仿真,尽管LEFT JOIN 的方法更理想,因为更多的RDBMS可以优化连接,而不是相关的子查询(尽管它们是等价的),而且我们可以重新使用现有的JOIN 树,以防多个列从共享路径中投影出来。

在当前的例子中,只有一个隐式连接的列,所以上述情况并不太重要:

UPDATE book
SET status = 'SOLD OUT'
WHERE (
  SELECT language.cd
  FROM language
  WHERE book.language_id = language.id
) = 'en';

DELETE FROM book
WHERE (
  SELECT language.cd
  FROM language
  WHERE book.language_id = language.id
) = 'en';

这种方法在每个RDBMS中都适用,也可以递归地适用于多个路径段。

使用DML JOIN

一些RDBMS在DML语句中也支持某种JOIN 语法,jOOQ可以利用这个。目前,只有MariaDB、MySQL、MemSQL在这样做,而且只针对UPDATE 语句:

UPDATE (book JOIN language AS l ON book.language_id = l.id)
SET book.status = 'SOLD OUT'
WHERE l.cd = 'en';

这与我们已经为SELECT 语句所做的事情基本相同。这在开箱即用的情况下是相当整洁的。事实上,它在jOOQ 3.17之前就已经工作了,只是我们没有正式支持它。

请注意,其他RDBMS支持多表DML语句,包括例如PostgreSQL,其UPDATE 语句有一个FROM 子句,或者DELETE 语句有一个USING 子句。不幸的是,这个FROM 子句只允许使用INNER JOIN 语义,所以有一些边缘情况还不能用这种语法来实现。

使用可更新的视图

有几个RDBMS支持标准的SQL可更新视图,包括可更新的内联视图。Oracle就是其中之一。在Oracle中,虽然不支持上述MySQL的UPDATE .. JOIN 语法,但可以做一些更强大的事情。

UPDATE (
  SELECT b.*, l.cd
  FROM book b
  LEFT JOIN language l ON b.language_id = l.id
) b
SET b.status = 'SOLD OUT'
WHERE b.cd = 'en'

虽然你已经可以用jOOQ手动使用这种语法,但jOOQ还不能将你的隐式JOIN 路径表达式翻译成上述语法,但我们很快就能做到,见#13917