Java JOOQ多表查询

Java JOOQ multiple tables query

我有问题。

我有以下查询:

SELECT
    Agents.Owner,
    Orders.*
FROM
    Orders
INNER JOIN Agents ON Agents.id = Orders.agentid
WHERE
    Agents.botstate = 'Active' AND Orders.state = 'Active' AND(
        Orders.status = 'Failed' OR Orders.status = 'Processing' AND Orders.DateTimeInProgressMicro < DATE_SUB(NOW(), INTERVAL 10 SECOND))
    ORDER BY
        Orders.agentid

但现在我需要将其转换为 JOOQ 语言。这是我想出的:

create.select()
        .from(DSL.table("Orders"))
        .join(DSL.table("Agents"))
        .on(DSL.table("Agents").field("Id").eq(DSL.table("Orders").field("AgentId")))
        .where(DSL.table("Agents").field("botstate").eq("Active")
        .and(DSL.table("Orders").field("state").eq("Active"))
        .and((DSL.table("Orders").field("status").eq("Failed"))
        .or(DSL.table("Orders").field("status").eq("Processing")))).fetch().sortAsc(DSL.table("Orders").field("AgentId"));

现在第一个问题是它不喜欢所有的 .eq() 语句,因为它给了我错误: Cannot resolve method: eq(Java.lang.String)。我的第二个问题是我不知道如何在JOOQ中写这个语句:Orders.DateTimeInProgressMicro < DATE_SUB(NOW(), INTERVAL 10 SECOND).

第一个问题是因为我不能只使用:

.on(Agents.Id).eq(Orders.AgentId)

但是我需要输入每个 table:

DSL.table("table_name")

并且对于每一列:

DSL.field("column_name")

否则它无法识别我的 table 和列

如何在 JOOQ 版本中正确编写 SQL 或者替代解决方案是我可以使用正常的 SQL 语句?

为什么您的代码不起作用?

Table.field(String) 不构造 table.field 形式的路径表达式。它试图从 Table 中取消引用 known 字段。如果 Table 没有任何 已知 字段(例如,在使用 DSL.table(String) 的情况下,则没有要取消引用的字段。

正确的普通 SQL API 用法

有两种类型的 API 允许使用动态 SQL 片段:

大多数人仅在无法生成代码时(见下文)或 jOOQ 缺少对供应商特定功能的某些支持(例如某些内置函数)时才使用它们。 以下是使用每个查询编写查询的方法:

普通 SQL API

API 的优点是您可以使用任意 SQL 片段,包括 jOOQ 未知的供应商特定函数调用。 运行存在语法错误、SQL 注入 (!) 和简单数据类型问题的一定风险,因为 jOOQ 不会知道数据类型,除非您明确告诉 jOOQ

// as always, this static import is implied:
import static org.jooq.impl.DSL.*;

然后:

create.select()
      .from("orders") // or table("orders")
      .join("agents") // or table("agents")
      .on(field("agents.id").eq(field("orders.id")))
      .where(field("agents.botstate").eq("Active"))
      .and(field("orders.state").eq("Active"))
      .and(field("orders.status").in("Failed", "Processing"))
      .orderBy(field("orders.agentid"))
      .fetch();

有时明确地告诉 jOOQ 数据类型是很有用的,例如在 SELECT 中使用这些表达式或创建绑定变量时:

// Use the default SQLDataType for a Java class
field("agents.id", Integer.class);

// Use an explicit SQLDataType
field("agents.id", SQLDataType.INTEGER);

姓名API

此 API 允许构建标识符(默认情况下引用,但您可以配置它,或使用 unquotedName())。如果标识符被引用,则可以避免 SQL 注入风险,但是在大多数方言中,您需要正确区分大小写。

create.select()
      .from(table(name("orders")))
      .join(table(name("agents")))
      .on(field(name("agents", "id")).eq(field(name("orders", "id"))))
      .where(field(name("agents", "botstate")).eq("Active"))
      .and(field(name("orders", "state")).eq("Active"))
      .and(field(name("orders", "status")).in("Failed", "Processing"))
      .orderBy(field(name("orders", "agentid")))
      .fetch();

使用代码生成器

一些用例阻止使用 jOOQ 的代码生成器,例如使用仅在 运行 时间已知的动态模式时。在所有其他情况下,非常强烈建议使用代码生成器。一般来说,使用 jOOQ 不仅可以更容易地构建 SQL 语句,而且您也不会 运行 遇到您在此处展示的问题。

您的查询将显示为:

create.select()
      .from(ORDERS)
      .join(AGENTS)
      .on(AGENTS.ID.eq(ORDERS.ID))
      .where(AGENTS.BOTSTATE.eq("Active"))
      .and(ORDERS.STATE.eq("Active"))
      .and(ORDERS.STATUS.in("Failed", "Processing"))
      .orderBy(ORDERS.AGENTID)
      .fetch();

好处:

  • 所有表和列都由您的 Java 编译器
  • 进行类型检查
  • 您可以在架构对象上使用 IDE 自动完成功能
  • 您永远不会 运行 陷入 SQL 注入问题或语法错误
  • 一旦重命名列或更改数据类型等,您的代码就会停止编译。
  • 获取数据时,您也已经知道数据类型
  • 您的绑定变量已使用正确的类型绑定,您无需明确指定

请记住,plain SQL API and the identifier API 都是为编译时架构未知的情况构建的,或者由于任何其他原因需要动态访问架构元素的情况。它们是低级 APIs,当代码生成是一个选项时要避免。