可用版本: Dev (3.21) | 最新 (3.20) | 3.19 | 3.18 | 3.17 | 3.16

这是一个实验性功能,因此可能会发生变化。使用风险自负!

替换

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

转换 SQL 的一个非常强大的方法是使用 org.jooq.QueryPart API 的 QueryPart.replace() API,将任何表达式树中的特定 org.jooq.QueryPart 元素替换为其他内容。此 API 将表达式树视为持久化数据结构,即生成的树可能包含现有树的部分,但不会修改现有树。

假设您希望实现一个优化引擎,该引擎删除冗余的 SQL 子句。例如,表达式 NOT(NOT(p)) 可以在标准 SQL 中替换为 p(在某些没有标准 BOOLEAN 类型支持的“聪明”方言中,它可能不完全相同)

// Contains redundant operators
Condition c = not(not(BOOK.ID.eq(1)));
System.out.println(c);
System.out.println(c.$replace(q ->
    q instanceof QOM.Not n1 && n1.$arg1() instanceof QOM.Not n2
        ? n2.$arg1()
        : q
));

以上打印

not (not ("BOOK"."ID" = 1))
"BOOK"."ID" = 1

替换算法将尝试在您的树上递归运行替换函数,直到它不再影响树。这意味着两件事

  • 您可以在一个函数中实现所有替换逻辑,适用于各种规则。规则的应用顺序是您在函数中定义的顺序。
  • 只有在没有更多规则适用时,算法才会停止。如果两个规则分别转换 A > BB > A,则该算法可能永远不会停止。

这是一个更复杂的示例,它使用 println() 调用记录替换

// Contains redundant operators
Condition c = not(not(not(BOOK.ID.ne(1))));
QueryPart result = c.$replace(q -> {
    if (q instanceof QOM.Not n1 && n1.$arg1() instanceof QOM.Not n2) {
        System.out.println("Replacing NOT(NOT(p)) by NOT(p): " + q);
        return n2.$arg1();
    }
    else if (q instanceof QOM.Not n1 && n1.$arg1() instanceof QOM.Ne<?> n2) {
        System.out.println("Replacing NOT(x != y) by x = y: " + q);
        return n2.$arg1().eq((Field) n2.$arg2());
    }

    return q;
}));
System.out.println("Result: " + result);

输出是

Replacing NOT(x != y) by x = y: not ("BOOK"."ID" <> 1)
Replacing NOT(NOT(p)) by NOT(p): not (not ("BOOK"."ID" = 1))
Result: "BOOK"."ID" = 1

如您所见

  • 替换函数被多次调用。
  • 第二次调用可以作用于第一次调用的结果,其中 NOT (x != y) 谓词已经得到改进。
  • 替换以递归方式工作,深度优先,自下而上。
  • 当不再进行替换时,它就会停止。

当您使用 jOOQ 的 解析器时,这显然也有效,并且通过 解析连接使用时非常有用,例如,优化任何类型的基于 JDBC 或 R2DBC 的应用程序!

// Contains redundant operators
Condition c = create.parser().parseCondition("not not not book.id != 1");
QueryPart result = c.$replace(q -> {
    if (q instanceof QOM.Not n1 && n1.$arg1() instanceof QOM.Not n2) {
        System.out.println("Replacing NOT(NOT(p)) by NOT(p): " + q);
        return n2.$arg1();
    }
    else if (q instanceof QOM.Not n1 && n1.$arg1() instanceof QOM.Ne<?> n2) {
        System.out.println("Replacing NOT(x != y) by x = y: " + q);
        return n2.$arg1().eq((Field) n2.$arg2());
    }

    return q;
}));
System.out.println("Result: " + result);

结果完全相同

Replacing NOT(x != y) by x = y: not (book.id <> 1)
Replacing NOT(NOT(p)) by NOT(p): not (not (book.id = 1))
Result: book.id = 1
从 jOOQ 3.17 开始,也可以使用监听 replacer来实现此日志记录。

内置 replacer

以下各节展示了一些内置 replacer 的示例。

限制

就像 模型 API 遍历一样,replacer 无法遍历到“不透明”的 org.jooq.QueryPart 实例中,包括 自定义 QueryParts纯 SQL 模板。另请参阅需要代码生成的功能了解更多详情。

反馈

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

The jOOQ Logo