• Mybatis条件语句 status != ““,status = 0时不生效


    Mybatis条件语句 status != “”,status = 0时不生效

    完整语句

    在这里插入图片描述

    复现步骤

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

    问题分析

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

    源码分析

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

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

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

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

        public BoundSql getBoundSql(Object parameterObject) {
          DynamicContext context = new DynamicContext(configuration, parameterObject);
          //将 语句内的内容划分为三部分:statisTextSqlNode、MixedSqlNode、TextSqlNode
          rootSqlNode.apply(context);
          .......
          return boundSql;
        }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

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

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

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

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

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

      org.apache.ibatis.scripting.xmltags.IfSqlNode#apply
      
      • 1
        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;
        }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      org.apache.ibatis.scripting.xmltags.ExpressionEvaluator
      
      • 1
        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;
        }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
        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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      1. 评估当前值如若未评估者去计算当前内容
      org.apache.ibatis.ognl.SimpleNode#getValue
      
      • 1
      1. 判断当前是否有常量值,如果存在则计算否则直接获取
      org.apache.ibatis.ognl.SimpleNode#evaluateGetValueBody
      //比如: 表达式 :id != ''、入参: id=0
      //常量值为:0(入参值)、''
      
      • 1
      • 2
      • 3
          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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      1. 表达式计算(比较)
      org.apache.ibatis.ognl.ASTEq#getValueBody
      
      • 1
      //表达式为: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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      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;
              }
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      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;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      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;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
  • 相关阅读:
    Python计算器(包含机制转换)
    78. 子集
    【网络安全 --- xss-labs靶场通关(11-20关)】详细的xss-labs靶场通关思路及技巧讲解,让你对xss漏洞的理解更深刻
    基于微信小程序的宠物寄养平台小程序设计与实现(源码+lw+部署文档+讲解等)
    弘辽科技:网店如何补流量?需要有什么准备?
    NAT模式和桥接模式的区别
    javaweb中的转发与重定向
    类和对象10:对象访问方法
    python使用pandas创建dataframe仿真数据、将字典数据转化为dataframe、index参数自定义指定dataframe数据的索引
    ViewConfiguration
  • 原文地址:https://blog.csdn.net/qq_40694145/article/details/126173928