引入命名参数会中断 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,例如:
- 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()
方法来替换数组。但是,我仍然建议您编写动态返回查询的函数。
要查询 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,例如:
- 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()
方法来替换数组。但是,我仍然建议您编写动态返回查询的函数。