regexp模式中的jooq绑定(MySQL)

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

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

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

为了让这个查询以编程方式工作,我必须用我想要查找的文档的名称替换正则表达式中的“TITLE_GOES_HERE”。然而,这在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;
}

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

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

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

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

abithluo

abithluo1#

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

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

解析规则

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

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

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

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:]+\\))")))

相关问题