Mybatis中数值类型条件语句使用 != ““ 的失效的场景

Mybatis当条件语句存在 status != “” 时, status = 0时失效效

前置条件未:status 为 数值类型

完整语句

在这里插入图片描述

复现步骤

id的条件语句中包含 id != “”,传参数的时候id设置的值为0

问题分析

有可能是因为Integer类型在和 “” 比较的时候,“” 会被默认转换成Integer类型去做比较,从而变成 id != 0,导致等式不成立

源码分析

我们直接从获取绑定sql中开始分析

org.apache.ibatis.mapping.MappedStatement#getBoundSql
  1. 获取绑定sql的关键方法
  public BoundSql getBoundSql(Object parameterObject) {
    //从这个方法我们就可以找到sql是如何被拼接的
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    ......
    return boundSql;
  }

看你写的是动态sql还是静态sql,我这边写的是动态sql

org.apache.ibatis.scripting.xmltags.DynamicSqlSource#getBoundSql
  1. 获取到 语句内的内容

      public BoundSql getBoundSql(Object parameterObject) {
        DynamicContext context = new DynamicContext(configuration, parameterObject);
        //将<select> </select> 语句内的内容划分为三部分:statisTextSqlNode、MixedSqlNode、TextSqlNode
        rootSqlNode.apply(context);
        .......
        return boundSql;
      }
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iznatl2q-1659668984079)(images/image-20220805095952470.png)]

  1. 留意解析 WhereSqlNode 节点的信息

    org.apache.ibatis.scripting.xmltags.MixedSqlNode#apply
    
  2. org.apache.ibatis.scripting.xmltags.TrimSqlNode#apply
    

    为什么会跳到TrimSqlNode节点?因为WhereSqlNode节点继承了,TrimSqlNode节点但是没有重写apply方法

      public boolean apply(DynamicContext context) {
        FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
        //获取当前节点的内容,然后继续调用父类的apply方法继续迭代解析
        boolean result = contents.apply(filteredDynamicContext);
        filteredDynamicContext.applyAll();
        return result;
      }
    
  3. 解析到if条件时会跳转到之类实现

    org.apache.ibatis.scripting.xmltags.IfSqlNode#apply
    
      public boolean apply(DynamicContext context) {
        //test: id != null and id != ''
        //context.getBindings() 包含全部入参
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
          contents.apply(context);
          return true;
        }
        return false;
      }
    
    org.apache.ibatis.scripting.xmltags.ExpressionEvaluator
    
      public boolean evaluateBoolean(String expression, Object parameterObject) {
        //通过Ognl去进行 表达式计算并缓存当前表达式
        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 static Object getValue(String expression, Object root) {
        try {
          Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null);
          // 解析成Ognl指定的表达式
          //使用Ognl去 计算表达式
          return Ognl.getValue(parseExpression(expression), context, root);
        } catch (OgnlException e) {
          ......
        }
      }
    
    1. 评估当前值如若未评估者去计算当前内容
    org.apache.ibatis.ognl.SimpleNode#getValue
    
    1. 判断当前是否有常量值,如果存在则计算否则直接获取
    org.apache.ibatis.ognl.SimpleNode#evaluateGetValueBody
    //比如: 表达式 :id != ''、入参: id=0
    //常量值为:0(入参值)、''
    
        protected Object evaluateGetValueBody(OgnlContext context, Object source) throws OgnlException {
            context.setCurrentObject(source);
            context.setCurrentNode(this);
            if (!this._constantValueCalculated) {
                this._constantValueCalculated = true;
                boolean constant = this.isConstant(context);
                if (constant) {
                    this._constantValue = this.getValueBody(context, source);
                }
    
                this._hasConstantValue = constant;
            }
    				//根据条件提供的表达式,在getValueBody(); 时会跳转 ASTEq#getValueBody
            return this._hasConstantValue ? this._constantValue : this.getValueBody(context, source);
        }
    
    1. 表达式计算(比较)
    org.apache.ibatis.ognl.ASTEq#getValueBody
    
    //表达式为:id != '' 
    protected Object getValueBody(OgnlContext context, Object source) throws OgnlException {
          //v1 = 0
       		Object v1 = this._children[0].getValue(context, source);
          //v2 = ''
          Object v2 = this._children[1].getValue(context, source);
          //在OgnlOps的底层中比较的时候,会将 0、'' 转换成double进行比较
          //注意当前equals,返回的结果集是相反的,对应xml中的表达式应该为:!OgnlOps.equal(v1, v2)
          return OgnlOps.equal(v1, v2) ? Boolean.TRUE : Boolean.FALSE;
     }
    
    1. 查看OgnlOps.equals()方法
        public static boolean equal(Object v1, Object v2) {
            if (v1 == null) {
                return v2 == null;
              //留意当前的 !isEquals
            } else if (v1 != v2 && !isEqual(v1, v2)) {
                if (v1 instanceof Number && v2 instanceof Number) {
                    return ((Number)v1).doubleValue() == ((Number)v2).doubleValue();
                } else {
                    return false;
                }
            } else {
                return true;
            }
        }
    
    public static boolean isEqual(Object object1, Object object2) {
         boolean result = false;
         if (object1 == object2) {
             result = true;
         } else if (object1 != null && object1.getClass().isArray()) {
              .........
         }else{
             //留意 compareWithConversion 方法
             result = object1 != null && object2 != null 
                && (object1.equals(object2) || compareWithConversion(object1, object2) == 0);
         }
          return result;
    }
    
    public static int compareWithConversion(Object v1, Object v2) {
        int result;
        if (v1 == v2) {
            result = 0;
        } else {
            // 0 = int, t1 = 4
            int t1 = getNumericType(v1);
            // '' = String,t2 = 10
            int t2 = getNumericType(v2);
            int type = getNumericType(t1, t2, true);
              switch (type) {
                case 6:
                    result = bigIntValue(v1).compareTo(bigIntValue(v2));
                    break;
                case 9:
                    result = bigDecValue(v1).compareTo(bigDecValue(v2));
                    break;
                case 10: //留意当前case 没有 break;
                    if (t1 == 10 && t2 == 10) {
                        if (v1 instanceof Comparable && v1.getClass().isAssignableFrom(v2.getClass())) {
                            result = ((Comparable)v1).compareTo(v2);
                            break;
                        }
    
                        throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and " + v2.getClass().getName());
                    }
                case 7:
                case 8:
                    // v1 = 0, dv1 = 0.0
                    double dv1 = doubleValue(v1);
                    //v2 = '', dv2 = 0.0
                    double dv2 = doubleValue(v2);
                    //返回结果集为 true,从而得到 
                    //OgnlOps.equal(0, '')  = true
                    return dv1 == dv2 ? 0 : (dv1 < dv2 ? -1 : 1);
                default:
                    long lv1 = longValue(v1);
                    long lv2 = longValue(v2);
                    return lv1 == lv2 ? 0 : (lv1 < lv2 ? -1 : 1);
            }
        }
    
        return result;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值