事件的起因是这样的,需要是按条件查数据而后给前端展现就行了,写的时候想着挺简略的,不就是应用 MyBatis 动静 SQL 去查问数据吗?

事实还是很残暴的,等我写完上完 UAT 后,前端同学说依据state查的数据与现实的数据不统一,这个state过后设计时只有两个值:01

/** * 数据状态 */@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.stateInteger型的,认真查看req.state != null没故障,而后发现req.state != ''应用Integer与空字符串做比拟。

前端在查问的时如果没有传递req.statereq.state != null 这里不会满足,然而前端传递了一个0过去的时候req.state != ''竟然返回的是false也就是说在MyBatis的if语法中0是等于空字符串的

{    "state": 0}

这样的比拟没有报错,也是有点想不通了,没方法只能去看MyBatis源码找出这起因。

查看 MyBatis 源码

MyBatis 其余源码的查找过程就不具体说了,这里间接找到XMLScriptBuilder类,找到if语法的解析过程,而后一步步的探索0 == ''的起因。 XMLScriptBuilder会解析trimif等 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获取材料