今天工作中发现一个Long类型的参数没有传到sql中去,在sql xml配置文件中是使用if test标签判断:

 xxxxx

通过debug发现参数中的version是有值的,但出来的sql语句就没有这个version


网上查了一些有不少同样这样的问题,大致解决办法分两种:

1、去掉空字符串判断

         xxxxx

2、添加0值判断

         xxxxx

这两种方法都是可以的,在我看来是这样,如果这个version类型和我的情况一样,是包装类型而不是基本数据类型的话,第一种就足够了,而且更贴近实际,因为包装类型除了有值的情况就是null,不会为""空字符串的,String类型不在我讨论的范围内,标题已经说了是数字0,况且如果是String的话就不会有这个问题了。


知道了怎么解决这个问题,那就想知道为什么这个问题会出现,当然要查看mybatis的源码了,

sql语句是通过获取BoundSql来的(网上查看mybatis层次结构),这个是通过方法getBoundSql(),这个方法定义在一个接口SqlSource,它有五个实现,从命名上看应该是找DynamicSqlSource,

暂时缺少图片

if判断的这种属于动态的sql所以直接找它了

这里面有句:rootSqlNode.apply(content);这个是添加动态sql 的,点进去查看

apply也是在一个接口里,查看实现,各种和标签有关的命名实现,这个标签是if当然就是找IfSqlNode了,它里面的实现是通过evaluator.evaluateBoolean()方法判断的,点进去继续看

发现一行重要的代码:

if(value instanceof Number) return !new BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO);

现在真相大白了,不管是Inteter还是Long,只要是值为0,都会判断为false


真的很抱歉,上面的分析错了,早上来了之后有四个浏览,立刻隐藏了这篇文章准备修改,希望那四位网友能再看到更正,上面的分析是在昨天晚上下班后,现在我又看了一遍昨天打开的源码:

上面的value其实是test后面的表达式的返回值,而不是传入的version参数,test后面的表达式如果只写一个0的话就会走上面的if判断,直接返回false,但眼前的情况是version为0,test后面的表达式就是:0 != null and 0 != '',这个表达式是由org.apache.ibatis.scripting.xmltags.OgnlCache类的getValue(String expression,Objec root)方法得来,下载了ognl源码包关联之后从新走的debug追踪了一下,还是从上面IfSqlNode中的apply方法判断说起:

(DynamicContext context) {
  (.evaluateBoolean(context.getBindings())) {
    .apply(context)}
  }

进入evaluateBoolean方法:

(String expressionObject parameterObject) {
  Object value = OgnlCache.(expressionparameterObject)(value Boolean) (Boolean) value(value Number) !BigDecimal(String.(value)).equals(BigDecimal.)value != }

这里面expression参数就是“version != null and version != ''”这个getValue方法里面就是调用Ognl包中的方法处理之后返回的一个值,进去这个getValue方法中

Object (String expressionObject root) {
  {
    Map context = Ognl.(rootOgnlCla***esolver())Ognl.((expression)contextroot)} (OgnlException e) {
    BuilderException(+ expression + + ee)}
}

这里面parseExpression(expression)是解析表达式的,没有决定到最终结果,还是要进去看getvalue方法,里面调用了好多层,debug下一步一步进去追踪,会发现下面的关键代码:

Object getValueBody(OgnlContext contextObject source) OgnlException {
    Object v1 = .children[].getValue(contextsource)Object v2 = .children[].getValue(contextsource)OgnlOps.equal(v1v2)?Boolean.FALSE:Boolean.TRUE}

上面的表达式是version != null and version != '',这个方法是在一个for循环里调用的,分别是0和null的比较,0和""的比较,第一次比较肯定是true不用说了,第二次的时候v1就是0,v2就是"",继续查看这里的OgnlOps.equal()方法

equal(Object v1Object v2) {
    v1 == ?v2 == :(v1 != v2 && !isEqual(v1v2)?(v1 Number && v2 Number?((Number)v1).doubleValue() == ((Number)v2).doubleValue():):)}

0和null判断为false,执行:后面的,关键取决于isEqual()方法了,进去这方法里又能看到一个方法

result = compareWithConversion(object1, object2, true) == 0 || object1.equals(object2);

进去查看

compareWithConversion(Object v1Object v2equals) {
    result(v1 == v2) {
        result = } {
            t1 = getNumericType(v1)t2 = getNumericType(v2)type = getNumericType(t1t2)(type) {
        :
            result = bigIntValue(v1).compareTo(bigIntValue(v2)):
            result = bigDecValue(v1).compareTo(bigDecValue(v2)):
            (t1 == && t2 == ) {
                (v1 != && v2 != ) {
                    (v1.getClass().isAssignableFrom(v2.getClass()) || v2.getClass().isAssignableFrom(v1.getClass())) {
                        (v1 Comparable) {
                            result = ((Comparable)v1).compareTo(v2)}

                        (equals) {
                            result = v1.equals(v2)?:}
                    }

                    (!equals) {
                        IllegalArgumentException(+ v1.getClass().getName() + + v2.getClass().getName())}

                    result = } {
                    var10000 = v1 != v2}
            }
        :
        :
                 dv1 = doubleValue(v1)dv2 = doubleValue(v2)dv1 == dv2?:(dv1 < dv2?-:):
            lv1 = longValue(v1)lv2 = longValue(v2)lv1 == lv2?:(lv1 < lv2?-:)}
    }

    result}

参数一个是0,一个是"",最终debug会走进case 8 里面,0和“”都会被转成double进行比较,都会变成0.0,这就是mybati中if test 0!=""判定为false的原因