regexp模式中的jooq绑定(MySQL)

xqkwcwgp  于 2023-04-29  发布在  Mysql
关注(0)|答案(1)|浏览(152)

我有一个小型的Java服务连接到一个MySQL DB,用来存储文档。每个文档都有一个名称,当一个文档被复制时,新文档的名称为<previous name> (#)。documents表如下所示:
| 身份证|名称|
| --------------|--------------|
| 1|A文件|
| 二|一份文件(1)|
| 三|第1102章一份文件(二)|
| 四|其他文件|
| 五|另一份文件(1)|
我有一个工作的MySQL查询,它选择了一个文档及其所有副本,我试图将其转换为jooq。

  1. select * from documents
  2. where name regexp '(TITLE_GOES_HERE|TITLE_GOES_HERE \\([:digit:]+\\))$';
  3. -- For instance, to select rows 1, 2, and 3 of the above table,
  4. -- I would replace `TITLE_GOES_HERE` with `A Document`, resulting
  5. -- in the following MySQL query:
  6. select * from documents
  7. where name regexp '(A Document|A Document \\([:digit:]+\\))$';

**注意:**如果你想要一个可用的正则表达式测试器,你可以找到它here。它与MySQL版本略有不同,但足以用于演示目的。

为了让这个查询以编程方式工作,我必须用我想要查找的文档的名称替换正则表达式中的“TITLE_GOES_HERE”。然而,这在jooq中抛出了一个意想不到的错误。

  1. public List<Document> getOriginalAndDuplicates(String docName) {
  2. SelectQuery<DocumentRecord> select = getDSLContext().selectQuery(DOCUMENT);
  3. select.addCondition(
  4. DSL.condition(
  5. DSL.field(
  6. // Binding for both the document AND the name
  7. // Notice that the name binding is inside the regular expression
  8. "{0} regexp '({1} \\\\([:digit:]+\\\\))'",
  9. Boolean.class,
  10. DOCUMENT.NAME,
  11. docName)));
  12. Result<DocumentRecord> records = select.fetch();
  13. List<Document> docs = records.stream().map(documentConverter::convertTo).toList();
  14. return docs;
  15. }

当我运行它时,我得到以下错误:

  1. 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.
  2. at org.jooq_3.14.16.MYSQL.debug(Unknown Source)
  3. at org.jooq.impl.Tools.translate(Tools.java:2903)
  4. at org.jooq.impl.DefaultExecuteContext.sqlException(DefaultExecuteContext.java:757)
  5. at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:389)
  6. ... 27 common frames omitted
  7. Caused by: java.sql.SQLException: Syntax error in regular expression on line 1, character 2.
  8. at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
  9. at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
  10. at com.mysql.cj.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:555)
  11. at com.mysql.cj.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:339)
  12. at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:354)
  13. at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
  14. at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java)
  15. at org.jooq.tools.jdbc.DefaultPreparedStatement.execute(DefaultPreparedStatement.java:214)
  16. at org.jooq.impl.Tools.executeStatementAndGetFirstResultSet(Tools.java:4217)
  17. at org.jooq.impl.AbstractResultQuery.execute(AbstractResultQuery.java:283)
  18. at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:375)
  19. ... 27 common frames omitted

发生错误的原因是第一个绑定({1})未被文档名称替换。
有人遇到过这样的事情吗?

**附加信息:**当我将{1}替换为硬编码值时,例如A Document,查询成功,没有任何问题。只有当我尝试将{1}绑定到变量值时才会出现问题。

abithluo

abithluo1#

为什么你的普通SQL模板不起作用

jOOQ plain SQL template不会在字符串文字或注解以及其他内容中进行任何替换。根据文件:

解析规则

在处理这些普通SQL模板时,会运行一个迷你解析器,它处理以下内容

  • 字符串文字
  • 引用的名字
  • 评论
  • JDBC转义序列
  • 索引(?)或命名(:identifier)绑定变量占位符

模板引擎会识别上述内容,并且在替换编号占位符和/或绑定变量时会忽略其中的内容。
你的模板应该这样写:

  1. DSL.field(
  2. "{0} regexp concat('(', {1}, '\\\\([:digit:]+\\\\))'",
  3. Boolean.class,
  4. DOCUMENT.NAME,
  5. docName
  6. )

请注意,所需的\\数量取决于NO_BACKSLASH_ESCAPES设置,您也可以在jOOQ中配置该设置。如果您没有使用普通的SQL模板,jOOQ会自动处理这个问题。

改用jOOQ API

但为什么要使用模板呢?jOOQ使用Field.likeRegex()对该运算符提供了本机支持:

  1. DOCUMENT.NAME.likeRegex(concat(val("("), val(docName), val("\\([:digit:]+\\))")))
展开查看全部

相关问题