【问题标题】:jOOQ CommonTableExpression with SelectQuery带有 SelectQuery 的 jOOQ CommonTableExpression
【发布时间】:2021-04-30 17:26:42
【问题描述】:

我们有一个在不同情况下使用的具有通用结构的查询(where 子句不同)

所以我们有这样的东西

val baseQuery: SelectQuery<Record> = dsl
  .select(someFields)
  .from(someTable)
  .join(otherTable).on(joinClause)
  .query

在其他地方,我们会根据需要扩展此查询。

baseQuery.apply {
  addConditions(conditionsA)
}.fetch()

baseQuery.apply {
  addConditions(conditionsB)
}.fetch()

到目前为止一切顺利。但是现在,如果我们能以某种方式将此基础与 CTE 结合使用,那就太酷了。不过不知道该怎么做。

val someCTE: CommonTableExpression<*> = DSL.....

// dsl
//   .with(someCTE)
//   .selectQuery(baseQuery.apply {}) ¯\_(ツ)_/¯

baseQuery.apply {
  // addWith(someCTE) ¯\_(ツ)_/¯
  addSelect(someCTE.field(cteField))
  addJoin(someCTE, joinClause)
  addConditions(conditionsC)
} 

有办法吗?也许其他建议如何在使用 CTE 时重用基本查询?

编辑:解决方案

在 Lukas 的回答的帮助下,我决定采用这种方法

fun dynamicQuery(
  context: SelectSelectStep<*> = dsl.select(),
  selects: List<Field<*>> = listOf(),
  joins: List<Pair<Table<*>, Condition>> = listOf(),
  conditions: List<Condition> = listOf()
): SelectQuery<Record>

所以在正常的自定义中我可以

dynamicQuery(
  conditions = conditionsA
).fetch()

dynamicQuery(
  conditions = conditionsB
).fetch()

它可以与 CTE 结合使用

val someCTE: CommonTableExpression<*> = DSL.....
dynamicQuery(
  context = dsl.with(someCTE).select(),
  selects = listOf(someCTE.field(cteField)),
  joins = listOf(someCTE to joinClause),
  conditions = conditionsC
).fetch()

【问题讨论】:

    标签: common-table-expression jooq


    【解决方案1】:

    TL;DR:您不能将 CTE 与 jOOQ 3.15 的模型 API 一起使用

    jOOQ 模型 API 与 DSL API 区别的一些背景知识

    非常古老的 jOOQ 1.0 只有现在所谓的“模型 API”,这是一个带有 setter(没有 getter)的可变过程 API,您可以在其中操作动态 SQL。

    jOOQ 2.0 引入了当今大多数人都在使用的 DSL API。 DSL API 模仿 SQL 语言这一事实​​有助于用户更轻松地发现 jOOQ API。一切都按预期命名。除了 CTE 和派生表领域的一些怪癖外,您几乎可以像编写实际 SQL 一样编写 jOOQ-SQL。

    模型 API 并未被弃用,而是由 DSL API 包装,并保留:

    • 出于向后兼容性的原因
    • 因为有些人似乎喜欢程序化方法

    你不能用模型 API 做任何你不能用 DSL API 做的事情,尽管在用 DSL API 做这件事时,更实用的编程风格可能会有所帮助。见:https://blog.jooq.org/2017/01/16/a-functional-programming-approach-to-dynamic-sql-with-jooq

    jOOQ 的未来

    虽然模型 API 仍在为 SELECTINSERTUPDATEDELETE 语句提供一些新的子句支持,但有些语句在模型 API 表单中不可用。这些包括MERGETRUNCATE、所有DDL statements、所有procedural statements。还有,WITH 子句。

    我们的策略是最终弃用模型 API,因为冗余会产生大量额外的工作,这些工作最好在其他地方投资。当人们以意想不到的顺序调用模型 API 方法时,也存在一些细微的错误,即无法通过 DSL API 实现的顺序。

    • 第一步,很快,jOOQ 将反转 API 之间的关系:https://github.com/jOOQ/jOOQ/issues/11241。模型 API 将成为 DSL API 的辅助包装器,供那些依赖它来实现向后兼容性的人使用。模型 API 甚至会被提取到单独的兼容性模块中,以阻止其在新代码中的使用,这种可能性不大
    • 在下一步中,通过反转依赖关系,DSL API 最终可以变得始终不可变,这是许多用户所期望的,但令他们惊讶的是,他们发现缺乏:https://github.com/jOOQ/jOOQ/issues/9047
    • 最终,模型 API 将被弃用,然后被删除

    你今天仍然可以使用它,并且弃用和删除将在很长一段时间内完成,所以不要急于关闭这个 API(与 jOOQ 一样)。但是在您的问题的背景下,很高兴看到 jOOQ 不会再投资于添加太多功能。 CTE 支持不会添加到模型 API。

    解决方法

    当然,您可以绕过这个限制,因为在内部,模型 API 支持 CTE:

    • 您可以使用反射将新的 CTE 添加到 SelectQuery 内部表示。我不会在这里记录它是如何工作的,因为记录这些东西从来都不是一个好主意:)
    • 您可以开始使用 DSL API 创建查询,然后使用 SelectFinalStep.getQuery() 提取内部 SelectQuery 表示,然后从那里继续工作。

    【讨论】:

    • 感谢您的澄清。不幸的是,没有关于如何在WithStepDSLContext 之间启动查询的通用接口。在构建动态查询时,我现在从SelectSelectStep 开始。所以在正常情况下,我以dslContext.select()....dynamicQuery 开始动态查询,而对于CTE 情况,我以dslContext.with().select()....dynamicQuery 开始。如果select() 查询启动方法可以在一个通用接口上,那么必须添加空的select() 会很酷,这有点遗憾。
    • @RanilWijeyratne:我理解这个限制。我认为目前不可能提出具体建议。我们必须为所有语句提取一个初始“步骤”,并让DSLContext 扩展所有这些初始步骤,这对于这种情况来说似乎是矫枉过正。但可能有更好的解决方案。我们应该为WITH 子句接受空集合或数组,然后不要生成任何此类子句。不幸的是,它目前不起作用:github.com/jOOQ/jOOQ/issues/11347。将立即解决此问题。
    • @RanilWijeyratne:3.14.7 发布了这个修复
    猜你喜欢
    • 2015-05-05
    • 2020-03-28
    • 2013-11-19
    • 2016-07-02
    • 1970-01-01
    • 2020-10-02
    • 2013-01-05
    • 2020-06-02
    • 2013-10-02
    相关资源
    最近更新 更多