【问题标题】:MyBatis Dynamic Recursive WHERE clauseMyBatis 动态递归 WHERE 子句
【发布时间】:2015-10-20 15:54:20
【问题描述】:

我想向我的前端公开使用在运行时指定的逻辑表达式进行搜索的能力(有限制以确保性能,但这是另一个主题)。也就是说,我想运行带有 WHERE 子句的查询,例如

WHERE ((email like 'test') AND (user_id=1 OR user_id>=1000)) OR (trans_id <100 AND session_id> 500)

事先不知道逻辑的树结构(愿望#1)。我还想使用 #{} 机制(它替换 SQL 字符串中的“?”并构建要传递给 PreparedStatement 的参数列表)来封装用户提供的值(希望 #2)。最后,我想使用 XML 查询构造而不是 SQLBuilder 构造,因为我正在使用基于前者的大量代码(愿望 #3)。

不幸的是,即使我希望 Mybatis 执行的任务完全在可能性范围内(它只是在 Java 对象的小层次结构上递归!) mybatis 的局限性似乎阻碍了我实现 #1, #2 和 #3 同时进行。希望我只是错过了一些东西。这是我尝试过的:

策略 A:将用户查询限制为“AND 的 OR”结构(放弃 #1)。熟悉 DeMorgan 定律的人会注意到,任何逻辑树都可以很容易地放入这种形式——但是,DeMorganated 语句不能干净地提供给 MyBatis,因为叶语句(将 AND 连接在一起的东西——例如 user_id =1) 没有固定数量的 #{} 参数,因此不能成为 foreach 的主题。这是MyBatis - how to create w dynamic WHERE Clause采取的策略

策略 B:只需使用 ${} 插入递归构造的 WHERE(放弃 #2)。相反,我必须清理我自己的用户输入(有点担心)并将其转换为字符串,这无疑会引入微妙的错误,其中 SQL 服务器的字符串转换和后续解析的行为与“?”不同。将对象带外传递给 JDBC 驱动程序的机制(高度关注)。

策略 C:将我的所有代码转换为 SQLBuilder 构造(放弃 #3)。我不喜欢这种方法,因为 SQLBuilder 似乎不是特别成熟——特别是,有一个公开的问题推测添加括号支持的策略,我无疑需要它。 https://github.com/mybatis/mybatis-3/issues/362

策略 D:递归构建 WHERE 子句,手动插入 ?标记并跟踪要传递给 PreparedStatement 的参数。手动将此字符串分块为包含一个 ?每个然后从 XML 中迭代它们。这会起作用,但非常难看。

<foreach item="block" index="i" collection="oneParamBlocks" open="" separator="" close="">${block.pfx}#{block.param}${block.sfx}</foreach>

策略 E(一厢情愿):有一种我不知道的机制来执行 ${whereClause} 替换,其中 whereClause 可以包含几个“?”字符和相应的对象是通过以某种方式用字符串打包来指定的(例如 getWhereClause() 返回一个包含字符串“WHERE user_name=?AND user_email=?”和参数数组 {"Bob","bob@example .com"})。


这是mybatis解析#{}的地方:https://github.com/mybatis/mybatis-3/blob/2d079fdd397f8a801679f701188be6e05ee9fdf5/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java

这是mybatis解析${}的地方:https://github.com/mybatis/mybatis-3/blob/2d079fdd397f8a801679f701188be6e05ee9fdf5/src/main/java/org/apache/ibatis/parsing/PropertyParser.java

如果你们都找不到我错过的东西,我可能会编写一些代码让我实施策略 E 并发送拉取请求。

【问题讨论】:

    标签: java sql recursion mybatis


    【解决方案1】:

    事实证明,将逻辑树“干净”地转换为令牌列表是可能的。关键观察:唯一直接包含需要过滤器相关数量#{}替换的未清理数据的过滤器是叶过滤器。内部过滤器(AND 和 OR)可以通过将字符串放入查询的通用标记类型来实现。这就是我们最终使用的:

    <foreach item="tok" collection="filter.tokenRepresentation" open="" separator="" close=""><choose>
            <when test="tok.mybatisChooseType == 'str'">${tok.hardcodedGlueStr}</when>
    
            <when test="tok.mybatisChooseType == &quot;&lt;&quot;">${tok.filter.validatedColumnName}&lt;#{tok.filter.unsanitizedLiteral}</when>
            <when test="tok.mybatisChooseType == &quot;&gt;&quot;">${tok.filter.validatedColumnName}&gt;#{tok.filter.unsanitizedLiteral}</when>
            ...
     </choose></foreach>
    

    tokenRepresentation 是通过直接的深度优先搜索构造的。 choose 和 foreach 之间没有间距会阻止在每个标记之间插入两个换行符。

    【讨论】:

      猜你喜欢
      • 2011-08-28
      • 1970-01-01
      • 2016-11-26
      • 2019-01-26
      • 2019-10-12
      • 2016-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多