引入命名参数会中断 jOOQ 查询

Introducing a named parameter breaks jOOQ query

要查询 PostgreSQL 10.11 数据库,我使用的是 jOOQ 3.12.4,它与 Spring Boot 2.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))

相反。

我该如何解决或解决这个问题?

代码重用方法的更好解决方案

But when I refactor my query and introduce a named parameter (to reuse the query in multiple parts of my code)

虽然您可以通过这种方式使用 jOOQ(当以非线程安全方式改变和重用 jOOQ 查询时要小心!),但通常建议以更实用的方式使用 jOOQ,例如:

您不会通过重新使用 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() 方法来替换数组。但是,我仍然建议您编写动态返回查询的函数。