java—引入命名参数将中断jooq查询

whlutmcx  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(336)

为了查询postgresql10.11数据库,我使用了jooq3.12.4,它与springboot2.2捆绑在一起。
假设我使用jooq构建了一个查询,如下所示:

final String[] ids = ...;

final var query = dslContext.selectFrom(MY_TABLE).where(MY_TABLE.ID.in(ids));
final Map<String, List<MyTable>> changeDomains = query.fetch().intoGroups(MY_TABLE.ID, MyTable.class);

这段代码运行良好,并产生预期的结果。但当我重构查询并引入一个命名参数(以便在代码的多个部分中重用查询)时,如下所示:

final String[] ids = ...;

final var query = dslContext.selectFrom(MY_TABLE).where(MY_TABLE.ID.in(param("ids")));
final Map<String, List<MyTable>> changeDomains = query.bind("ids", ids).fetch().intoGroups(MY_TABLE.ID, MyTable.class);

我突然发现以下错误:

org.springframework.jdbc.BadSqlGrammarException: jOOQ; bad SQL grammar ...; nested exception is org.postgresql.util.PSQLException: ERROR: operator does not exist: text = character varying[]
    Hinweis: No operator matches the given name and argument type(s). You might need to add explicit type casts.

编辑:当我使用

MY_TABLE.ID.in(param("ids", String[].class))

相反。
如何解决或解决这个问题?

vd2z7a6w

vd2z7a6w1#

更好的代码重用解决方案

但是当我重构查询并引入一个命名参数时(在代码的多个部分中重用查询)
虽然您可以这样使用jooq(在以非线程安全的方式变异和重用jooq查询时要小心!),通常建议以更实用的方式使用jooq,例如:
https://blog.jooq.org/2017/01/16/a-functional-programming-approach-to-dynamic-sql-with-jooq/
https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/
通过重新使用jooq查询,您不会获得太多好处,特别是,几乎没有任何性能提升。
所以,与其这样:

final var query = dslContext.selectFrom(MY_TABLE)
    .where(MY_TABLE.ID.in(param("ids")));
final Map<String, List<MyTable>> changeDomains = query
    .bind("ids", ids).fetch().intoGroups(MY_TABLE.ID, MyTable.class);

写下:

public ResultQuery<MyTableRecord> query(String[] ids) {
    return dslContext.selectFrom(MY_TABLE).where(MY_TABLE.ID.in(ids));
}

// And then:
final Map<String, List<MyTable>> changeDomains = query(ids)
    .fetch().intoGroups(MY_TABLE.ID, MyTable.class);

您遇到的实际问题:

jooq、jdbc和sql不支持单绑定值 IN 列表。虽然这样写似乎很有用:

SELECT * FROM t WHERE c IN (:bind_value)

而将数组或列表作为单个绑定值传递,这在sql中是不受支持的。有些api可能会假装支持这个功能(但在幕后,它会将单个绑定值替换为多个绑定值) ?, ?, ..., ? postgresql支持 = ANY (:bind_value) 带数组的运算符

SELECT * FROM t WHERE c = ANY (:bind_value)

你可以在jooq中使用

dslContext.selectFrom(MY_TABLE).where(MY_TABLE.ID.eq(any(ids)));

那样的话,你可以打电话给 bind() 方法在执行之前替换数组。但是,我仍然建议您编写动态返回查询的函数。

相关问题