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

SQL 到 DSL 映射规则

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

jOOQ 将 SQL 作为外部领域特定语言,并将其映射到 Java,从而创建一个内部领域特定语言。内部 DSL 无法 100% 实现其外部语言对应部分,因为它们必须遵守其宿主或目标语言(即 Java)的语法规则。本节解释了 jOOQ 中遇到的各种问题和解决方法。

SQL 允许“无关键字”语法

SQL 语法并不总是需要关键字来构成表达式。UPDATE .. SET 子句接受各种参数赋值

UPDATE t SET a = 1, b = 2
update(t).set(a, 1).set(b, 2)

上面的示例还显示了缺少运算符重载功能,其中 jOOQ 中 "=""," 替换。 另一个例子是 行值表达式,只能在 SQL 中使用括号形成

(a, b) IN ((1, 2), (3, 4))
row(a, b).in(row(1, 2), row(3, 4))

在这种情况下,ROW 是一个实际的(可选的)SQL 关键字,至少由 PostgreSQL 实现。

SQL 包含“复合”关键字

与大多数语言一样,SQL 不对空格赋予任何意义。 但是,在形成“复合”关键字时,空格非常重要,即由多个关键字组成的 SQL 子句。 jOOQ 遵循标准的 Java 方法命名约定,将 SQL 关键字(不区分大小写)映射到 Java 方法(区分大小写,驼峰式命名)。 一些例子

GROUP BY
ORDER BY
WHEN MATCHED THEN UPDATE
groupBy()
orderBy()
whenMatchedThenUpdate()

未来的 jOOQ 版本可能会使用全大写的方法名称,以及驼峰式命名(以防止与 Java 关键字冲突)

GROUP BY
ORDER BY
WHEN MATCHED THEN UPDATE
GROUP_BY()
ORDER_BY()
WHEN_MATCHED_THEN_UPDATE()

SQL 包含“多余”关键字

一些 SQL 关键字并不是真正必要的。 它们只是一个关键字丰富的语言的一部分,Java 开发人员已经不再习惯。 这些关键字可以追溯到 ADA、BASIC、COBOL、FORTRAN、PASCAL 等语言更冗长的时候

  • BEGIN .. END
  • REPEAT .. UNTIL
  • IF .. THEN .. ELSE .. END IF

当在 Java 中编写这些关键字太繁琐时,jOOQ 会省略其中一些关键字。

CASE WHEN .. THEN .. END
decode().when(.., ..)

上面的示例在 Java 中省略了 THENEND 关键字。 但是,为了提供与 SQL 语言更 1:1 的匹配,未来的 jOOQ 版本可能会包含一个更完整的 DSL,再次包括这些关键字。

SQL 包含“多余”的语法元素

一些 SQL 构造很难映射到 Java,但它们也不是真正必要的。 SQL 通常期望在语法上使用括号,而在这些括号不是真正需要的,或者与 SQL 语言的其余部分略有不一致。

LISTAGG(a, b) WITHIN GROUP (ORDER BY c)
              OVER (PARTITION BY d)
listagg(a, b).withinGroupOrderBy(c)
             .over().partitionBy(d)

用于 WITHIN GROUP (..)OVER (..) 子句的括号在 SQL 中是必需的,但似乎没有增加任何直接价值。 在某些情况下,jOOQ 会省略它们,尽管上述内容可能会在将来进行可选的重新表述,以形成更像 SQL 的体验

LISTAGG(a, b) WITHIN GROUP (ORDER BY c)
              OVER (PARTITION BY d)
listagg(a, b).withinGroup(orderBy(c))
             .over(partitionBy(d))

SQL 使用了一些 Java 的保留字

如果使用驼峰式命名进行映射,一些 SQL 关键字会映射到 Java 语言关键字。 这些关键字目前包括

  • CASE
  • ELSE
  • FOR

jOOQ 在这些关键字上使用后缀来防止冲突

CASE .. ELSE
PIVOT .. FOR .. IN ..
case_() .. else_()
pivot(..).for_(..).in(..)

未来还有更多冲突的可能性,每个冲突都通过后缀解决

  • BOOLEAN
  • CHAR
  • DEFAULT
  • DOUBLE
  • ENUM
  • FLOAT
  • IF
  • INT
  • LONG
  • PACKAGE

SQL 运算符无法在 Java 中重载

大多数 SQL 运算符必须映射到 Java 中描述性的方法名称,因为 Java 不允许运算符重载

=
<>, !=
||
SET a = b
equal(), eq()
notEqual(), ne()
concat()
set(a, b)

对于那些使用 jOOQ with Scala 或 Groovy 的用户,可以利用运算符重载和隐式转换来增强 jOOQ

=
<>, !=
||
===
<>, !==
||

SQL 的先引用后声明能力

与其说这是一个 SQL 语法特性,不如说它是一个语义特性。 在 SQL 中,对象可以在声明之前(即“按字典顺序”)被引用。 对于 别名来说尤其如此

SELECT t.a
FROM my_table t
MyTable t = MY_TABLE.as("t");
select(t.a).from(t)

一个更复杂的例子是公共表表达式 (CTE),jOOQ 目前不支持它

WITH t(a, b) AS (
  SELECT 1, 2 FROM DUAL
)
SELECT t.a, t.b
FROM t

公共表表达式定义一个“派生列列表”,就像 表别名可以做的那样。 因此创建的正式记录类型无法通过 Java 编译器进行类型安全验证,即不可能从 t 中正式取消引用 t.a

引用此页

反馈

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

The jOOQ Logo