我想向我的前端公开使用在运行时指定的逻辑表达式进行搜索的能力(限制以确保性能,但这是另一个主题)。也就是说,我想用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 :将用户查询限制为“OR的AND”结构(放弃#1)。那些熟悉DeMorgan定律的人会注意到任何逻辑树都可以很容易地放到这个形式中 - 但是,DeMorganated语句不能干净地提供给MyBatis,因为叶子语句(得到AND的东西 - 例如user_id = 1)没有固定数量的#{}参数,因此不能成为foreach的主题。这是MyBatis - how to create w dynamic WHERE Clause
中采取的策略策略B :只需使用$ {}插入递归构造的WHERE(放弃#2)。我必须改为清理自己的用户输入(略微关注)并将其转换为字符串,毫无疑问会引入细微的错误,其中SQL Server的字符串转换和后续解析与“?”的行为不同将带外对象传递给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}替换where 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/parsing/PropertyParser.java
的地方如果你们都找不到我错过的东西,我可能会编写一些代码来让我实施策略E并发送拉取请求。
答案 0 :(得分:0)
事实证明,可以“干净地”将逻辑树转换为令牌列表。关键观察:直接包含需要依赖于过滤器数量的#{}替换的未经过数据处理的数据的唯一过滤器是叶过滤器。内部过滤器(AND和OR)可以通过将字符串放入查询的通用令牌类型来实现。这就是我们最终使用的内容:
<foreach item="tok" collection="filter.tokenRepresentation" open="" separator="" close=""><choose>
<when test="tok.mybatisChooseType == 'str'">${tok.hardcodedGlueStr}</when>
<when test="tok.mybatisChooseType == "<"">${tok.filter.validatedColumnName}<#{tok.filter.unsanitizedLiteral}</when>
<when test="tok.mybatisChooseType == ">"">${tok.filter.validatedColumnName}>#{tok.filter.unsanitizedLiteral}</when>
...
</choose></foreach>
tokenRepresentation由直接深度优先搜索构成。 choose和foreach之间缺少间距会阻止在每个令牌之间插入两个换行符。