是否可以更改 jOOQ DSL 查询的 SELECT/WHERE
Posted
技术标签:
【中文标题】是否可以更改 jOOQ DSL 查询的 SELECT/WHERE【英文标题】:Is it possible to alter the SELECT/WHERE of jOOQ DSL query 【发布时间】:2021-11-03 21:38:18 【问题描述】:我想操作一个 jOOQ DSL 查询来改变它的 SELECT 列和 WHERE 条件。
例如:
DSLContext ctx = ...;
SelectHavingStep query = ctx.select(MyEntity.MY_ENTITY.ZIP, DSL.count(MyEntity.MY_ENTITY.ZIP))
.from(MyEntity.MY_ENTITY)
.where(MyEntity.MY_ENTITY.ID.gt("P"))
.groupBy(MyEntity.MY_ENTITY.ZIP);
用例 1:
我想将上述查询传递给一个实用程序类,该实用程序类将使用不同的 SELECT 生成相同的查询,例如:
ctx.select(DSL.count())
.from(MyEntity.MY_ENTITY)
.where(MyEntity.MY_ENTITY.ID.gt("P"))
.groupBy(MyEntity.MY_ENTITY.ZIP);
这个特殊的例子是为了能够创建显示查询总行数的分页结果。
用例 2:
我想将上述查询传递给一个实用程序类,该实用程序类将使用修改后的 WHERE 子句生成相同的查询,例如:
SelectHavingStep query =
ctx.select(MyEntity.MY_ENTITY.ZIP, DSL.count(MyEntity.MY_ENTITY.ZIP))
.from(MyEntity.MY_ENTITY)
.where(
MyEntity.MY_ENTITY.ID.gt("P")
.and(MyEntity.MY_ENTITY.ZIP.in("100", "200", "300"))
)
.groupBy(MyEntity.MY_ENTITY.ZIP);
这个特定示例是为了进一步限制基于某些条件的查询(即基于执行查询的用户的数据可见性)。
这可能吗?
目前,我在应用程序代码中的查询构造时使用帮助类来执行此操作。我想将责任转移到图书馆,以便可以透明地对应用程序执行。
谢谢。
【问题讨论】:
【参考方案1】:您不应该尝试更改 jOOQ 对象,而应该尝试以功能方式动态创建它们。有多种方法可以实现您的用例,例如
用例 1:
可以在此处查看通用分页查询的方法:https://blog.jooq.org/calculating-pagination-metadata-without-extra-roundtrips-in-sql/
理想情况下,您将避免COUNT(*)
查询的额外往返并使用COUNT(*) OVER ()
窗口函数。如果这在您的 SQL 方言中不可用,那么您可以这样做:
public ResultQuery<Record> mySelect(
boolean count,
Supplier<List<Field<?>>> select,
Function<? super SelectFromStep<Record>, ? extends ResultQuery<Record>> f
)
return f.apply(count ? ctx.select(count()) : ctx.select(select.get()));
然后像这样使用它:
mySelect(false,
() -> List.of(MY_ENTITY.ZIP),
q -> q.from(MY_ENTITY)
.where(MY_ENTITY.ID.gt("P"))
.groupBy(MY_ENTITY.ZIP)
).fetch();
这只是一种方法。还有很多其他的,请参阅下面的链接。
用例 2:
只需将上述示例更进一步,提取用于在另一个函数中创建WHERE
子句的逻辑,例如
public Condition myWhere(Function<? super Condition, ? extends Condition> f)
return f.apply(MY_ENTITY.ID.gt("P"));
现在使用如下:
mySelect(false,
() -> List.of(MY_ENTITY.ZIP),
q -> q.from(MY_ENTITY)
.where(myWhere(c -> c.and(MY_ENTITY.ZIP.in("100", "200", "300")))
.groupBy(MY_ENTITY.ZIP)
).fetch();
同样,有许多不同的方法可以解决这个问题,具体取决于什么是“公共部分”,以及什么是“用户定义的部分”。您还可以对 MY_ENTITY
表进行抽象并传递生成实际表的函数。
更多信息
另请参阅以下资源:
https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/ https://blog.jooq.org/a-functional-programming-approach-to-dynamic-sql-with-jooq/【讨论】:
卢卡斯,感谢您的及时回复。目前,我正在做类似于您建议的事情,使用提供包装器功能(用于条件)并强制调用包装器的命令模式。我的想法是事后(在创建查询之后)这样做,以减少开发人员错误将包装器放在错误位置的机会。再次感谢。 @AlejandroAbdelnur:嗯,您始终可以通过DSLContext.selectQuery()
使用“经典”模型 API,它会生成(仅附加的)可变对象。我一般不推荐它,因为:1)它会在(非常)长期内被移除,2)它远不如 FP 方法优雅。
我有一个案例,一个辅助函数想要同时添加一个 JOIN 子句和一个 WHERE 子句项,我不想让调用者担心这个,我只想申请导致将这两个级别的项目插入查询树的逻辑。因此,当您尝试消除冗余代码和出错机会时,动态查询并不是一条友好的路径。以上是关于是否可以更改 jOOQ DSL 查询的 SELECT/WHERE的主要内容,如果未能解决你的问题,请参考以下文章