事件的起因是这样的,需要是按条件查数据而后给前端展现就行了,写的时候想着挺简略的,不就是应用 MyBatis 动静 SQL 去查问数据吗?
事实还是很残暴的,等我写完上完 UAT 后,前端同学说依据state
查的数据与现实的数据不统一,这个state
过后设计时只有两个值:0
和1
。
/** * 数据状态 */@Range(min = 0, max = 1, message = "状态只能为0(未解决),1(已解决)")private Integer state;
现实状况下通过前端传递过去的值,而后进行sql查问就能够了:
<if test="req.state != null and req.state != ''"> AND md.state = #{req.state}</if>
下面的sql首先判断state
不为空且不为空字符串时,而后增加比拟state
字段。初步看下来if
判断没什么问题,然而我传递进去的req.state
是Integer
型的,认真查看req.state != null
没故障,而后发现req.state != ''
应用Integer
与空字符串做比拟。
前端在查问的时如果没有传递req.state
那req.state != null
这里不会满足,然而前端传递了一个0
过去的时候req.state != ''
竟然返回的是false
也就是说在MyBatis的if语法中0是等于空字符串的:
{ "state": 0}
这样的比拟没有报错,也是有点想不通了,没方法只能去看MyBatis源码找出这起因。
查看 MyBatis 源码
MyBatis 其余源码的查找过程就不具体说了,这里间接找到XMLScriptBuilder
类,找到if
语法的解析过程,而后一步步的探索0 == ''
的起因。 XMLScriptBuilder
会解析trim
、if
等 MyBatis 反对的语法,它的解析原理是通过NodeHandler
来别离解析不同的标签:
private void initNodeHandlerMap() { nodeHandlerMap.put("trim", new TrimHandler()); nodeHandlerMap.put("where", new WhereHandler()); nodeHandlerMap.put("set", new SetHandler()); nodeHandlerMap.put("foreach", new ForEachHandler()); nodeHandlerMap.put("if", new IfHandler()); nodeHandlerMap.put("choose", new ChooseHandler()); nodeHandlerMap.put("when", new IfHandler()); nodeHandlerMap.put("otherwise", new OtherwiseHandler()); nodeHandlerMap.put("bind", new BindHandler()); }
因为是不正解的语法是if
标签,查看IfHandler
就好了,其余当初略过就好。
private class IfHandler implements NodeHandler { public IfHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String test = nodeToHandle.getStringAttribute("test"); IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test); targetContents.add(ifSqlNode); } }
MyBatis会将if
标签形象成IfSqlNode
:
public class IfSqlNode implements SqlNode { private final ExpressionEvaluator evaluator; private final String test; private final SqlNode contents; public IfSqlNode(SqlNode contents, String test) { this.test = test; this.contents = contents; this.evaluator = new ExpressionEvaluator(); } @Override public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false; }}
终于有一点眉头了, MyBatis 会将if
标签的test
属性应用ExpressionEvaluator
测试一下是否为true
或者为false
:
public class ExpressionEvaluator { public boolean evaluateBoolean(String expression, Object parameterObject) { Object value = OgnlCache.getValue(expression, parameterObject); if (value instanceof Boolean) { return (Boolean) value; } if (value instanceof Number) { return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0; } return value != null; } public Iterable<?> evaluateIterable(String expression, Object parameterObject) { Object value = OgnlCache.getValue(expression, parameterObject); if (value == null) { throw new BuilderException("The expression '" + expression + "' evaluated to a null value."); } if (value instanceof Iterable) { return (Iterable<?>) value; } if (value.getClass().isArray()) { // the array may be primitive, so Arrays.asList() may throw // a ClassCastException (issue 209). Do the work manually // Curse primitives! :) (JGB) int size = Array.getLength(value); List<Object> answer = new ArrayList<Object>(); for (int i = 0; i < size; i++) { Object o = Array.get(value, i); answer.add(o); } return answer; } if (value instanceof Map) { return ((Map) value).entrySet(); } throw new BuilderException("Error evaluating expression '" + expression + "'. Return value (" + value + ") was not iterable."); }}
最初失去论断:Mybatis 应用的 Ognl表达式
来获取 test 属性的值
最终论证
曾经晓得 MyBatis 外部是应用的 Ognl表达式
,是不是 Ognl表达式
的引起的呢? 实际一下就晓得了,先引入依赖:
<!-- https://mvnrepository.com/artifact/ognl/ognl --><dependency> <groupId>ognl</groupId> <artifactId>ognl</artifactId> <version>2.7.3</version></dependency>
写程序测试:
public static void main(String[] args) { Map<String, Object> objectMap = new HashMap<>(); objectMap.put("state", "0"); Object value = OgnlCache.getValue("state != null and state != ''", objectMap); System.out.println(value); }
下面程序输入的真的是true
。。。
总结
真是脑袋抽筋啊,Integer
还判断是否为空字符串。。。
记录此坑,心愿对大家有所帮忙。
举荐
- 如果MySQL磁盘满了,会产生什么?
- TCP 三次握手、四手挥手,这样说你能明确吧!
- Netflix 微服务架构设计解析
欢送关注公众号:架构文摘,取得独家整顿120G的收费学习资源助力你的架构师学习之路!公众号后盾回复
arch028
获取材料