英文:
jooq binding within regexp pattern (MySQL)
问题
I have a small Java service connected to a MySQL DB which stores documents. Each document has a name, and when a document is duplicated, the new document gets the name <previous name> (#)
. The documents table looks something like this:
id | name |
---|---|
1 | A Document |
2 | A Document (1) |
3 | A Document (2) |
4 | Another Document |
5 | Another Document (1) |
I have a working MySQL query which selects a document and all of its duplicates, which I am attempting to translate into jooq.
select * from documents
where name regexp '(TITLE_GOES_HERE|TITLE_GOES_HERE \\([:digit:]+\\))$';
-- For instance, to select rows 1, 2, and 3 of the above table,
-- I would replace `TITLE_GOES_HERE` with `A Document`, resulting
-- in the following MySQL query:
select * from documents
where name regexp '(A Document|A Document \\([:digit:]+\\))$';
Note: If you would like a working regex tester, you can find it here. It's slightly different than the MySQL version, but it suffices for demonstration purposes.
In order for this query to work programmatically, I have to replace "TITLE_GOES_HERE" in the regular expression with the name of the document for which I want to find. However, that throws an unexpected error in jooq.
public List<Document> getOriginalAndDuplicates(String docName) {
SelectQuery<DocumentRecord> select = getDSLContext().selectQuery(DOCUMENT);
select.addCondition(
DSL.condition(
DSL.field(
// Binding for both the document AND the name
// Notice that the name binding is inside the regular expression
"{0} regexp '({1} \\([:digit:]+\\))'",
Boolean.class,
DOCUMENT.NAME,
docName)));
Result<DocumentRecord> records = select.fetch();
List<Document> docs = records.stream().map(documentConverter::convertTo).toList();
return docs;
}
When I run this, I get the following error:
Caused by: org.jooq.exception.DataAccessException: SQL [select document.id, document.name from document where (document.name regexp '({1} \\([:digit:]+\\))')]; Syntax error in regular expression on line 1, character 2.
at org.jooq_3.14.16.MYSQL.debug(Unknown Source)
at org.jooq.impl.Tools.translate(Tools.java:2903)
at org.jooq.impl.DefaultExecuteContext.sqlException(DefaultExecuteContext.java:757)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:389)
... 27 common frames omitted
Caused by: java.sql.SQLException: Syntax error in regular expression on line 1, character 2.
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:555)
at com.mysql.cj.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:339)
at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:354)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java)
at org.jooq.tools.jdbc.DefaultPreparedStatement.execute(DefaultPreparedStatement.java:214)
at org.jooq.impl.Tools.executeStatementAndGetFirstResultSet(Tools.java:4217)
at org.jooq.impl.AbstractResultQuery.execute(AbstractResultQuery.java:283)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:375)
... 27 common frames omitted
The error is occurring because the first binding ({1}
) is not being replaced with the document name.
Has anyone ever encountered anything like this?
英文:
I have a small Java service connected to a MySQL DB which stores documents. Each document has a name, and when a document is duplicated, the new document gets the name <previous name> (#)
. The documents table looks something like this:
id | name |
---|---|
1 | A Document |
2 | A Document (1) |
3 | A Document (2) |
4 | Another Document |
5 | Another Document (1) |
I have a working MySQL query which selects a document and all of its duplicates, which I am attempting to translate into jooq.
select * from documents
where name regexp '(TITLE_GOES_HERE|TITLE_GOES_HERE \\([:digit:]+\\))$';
-- For instance, to select rows 1, 2, and 3 of the above table,
-- I would replace `TITLE_GOES_HERE` with `A Document`, resulting
-- in the following MySQL query:
select * from documents
where name regexp '(A Document|A Document \\([:digit:]+\\))$';
> Note: If you would like a working regex tester, you can find it here. It's slightly different than the MySQL version, but it suffices for demonstration purposes.
In order for this query to work programmatically, I have to replace "TITLE_GOES_HERE" in the regular expression with the name of the document for which I want to find. However, that throws an unexpected error in jooq.
public List<Document> getOriginalAndDuplicates(String docName) {
SelectQuery<DocumentRecord> select = getDSLContext().selectQuery(DOCUMENT);
select.addCondition(
DSL.condition(
DSL.field(
// Binding for both the document AND the name
// Notice that the name binding is inside the regular expression
"{0} regexp '({1} \\\\([:digit:]+\\\\))'",
Boolean.class,
DOCUMENT.NAME,
docName)));
Result<DocumentRecord> records = select.fetch();
List<Document> docs = records.stream().map(documentConverter::convertTo).toList();
return docs;
}
When I run this, I get the following error:
Caused by: org.jooq.exception.DataAccessException: SQL [select document.id, document.name from document where (document.name regexp '({1} \\([:digit:]+\\))')]; Syntax error in regular expression on line 1, character 2.
at org.jooq_3.14.16.MYSQL.debug(Unknown Source)
at org.jooq.impl.Tools.translate(Tools.java:2903)
at org.jooq.impl.DefaultExecuteContext.sqlException(DefaultExecuteContext.java:757)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:389)
... 27 common frames omitted
Caused by: java.sql.SQLException: Syntax error in regular expression on line 1, character 2.
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:555)
at com.mysql.cj.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:339)
at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:354)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java)
at org.jooq.tools.jdbc.DefaultPreparedStatement.execute(DefaultPreparedStatement.java:214)
at org.jooq.impl.Tools.executeStatementAndGetFirstResultSet(Tools.java:4217)
at org.jooq.impl.AbstractResultQuery.execute(AbstractResultQuery.java:283)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:375)
... 27 common frames omitted
The error is occurring because the first binding ({1}
) is not being replaced with the document name.
Has anyone ever encountered anything like this?
> Additional info: When I replace the {1}
with a hardcoded value, such as A Document
, the query succeeds without any problems. It only causes problems when I try to bind the {1}
to a variable value.
答案1
得分: 1
I'll provide translations for the non-code parts:
为什么你的普通SQL模板不起作用
jOOQ 普通SQL模板 不会在字符串文字或注释中进行任何替换,以及其他内容。根据文档:
> ## 解析规则
> 在处理这些普通SQL模板时,会运行一个小型解析器,处理诸如
> - 字符串文字
> - 引用名称
> - 注释
> - JDBC转义序列
> - 索引 (?) 或命名 (:标识符) 绑定变量占位符
>
> 上述内容由模板引擎识别,并在替换编号占位符和/或绑定变量时忽略其中的内容。
您的模板必须编写如下:
DSL.field(
"{0} regexp concat('(', {1}, '\\\\([:digit:]+\\\\))'",
Boolean.class,
DOCUMENT.NAME,
docName
)
请注意,所需的 \\
数量取决于 NO_BACKSLASH_ESCAPES
设置,您还可以在 jOOQ 中配置此设置。如果您不使用普通SQL模板,jOOQ会自动处理此问题。
使用jOOQ API而不是模板
但为什么要使用模板呢?jOOQ对此运算符具有本机支持,可使用 Field.likeRegex()
来实现:
DOCUMENT.NAME.likeRegex(concat(val("("), val(docName), val("\\([:digit:]+\\))")))
英文:
Why your plain SQL template didn't work
A jOOQ plain SQL template will not make any substitutions in string literals or comments, and other things. As per the documentation:
> ## Parsing rules
> When processing these plain SQL templates, a mini parser is run that handles things like
> - String literals
> - Quoted names
> - Comments
> - JDBC escape sequences
> - Indexed (?) or named (:identifier) bind variable placeholders
>
> The above are recognised by the templating engine and contents inside of them are ignored when replacing numbered placeholders and/or bind variables.
Your template has to be written like this:
DSL.field(
"{0} regexp concat('(', {1}, '\\\\([:digit:]+\\\\))'",
Boolean.class,
DOCUMENT.NAME,
docName
)
Note the number of \\
needed depends on the NO_BACKSLASH_ESCAPES
setting, which you can also configure in jOOQ. jOOQ handles this automatically if you're not using a plain SQL template.
Using jOOQ API instead
But why bother using a template? jOOQ has native support for this operator using Field.likeRegex()
:
DOCUMENT.NAME.likeRegex(concat(val("("), val(docName), val("\\([:digit:]+\\))")))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论