Mybatis 关于Bigdecimal类型 动态SQL != "" 导致不能更新(赋值)的问题,mybatis的动态SQL中""会被解析成0

太长不看系列:在mybatis的动态SQL中""会被解析成0;

在mybtais中我们一般会用动态SQL处理自己的语句,也就是可以通过if语句判断是否成立,然后在进行赋值操作,
比如:

         <if test="item.useNumDecimal != null and item.useNumDecimal != '' " >
                use_num_decimal = #{item.useNumDecimal},
            </if>

这里的意思就是判断useNumDecimal不是null并且也不等于 '' ",然后就把useNumDecimal的值赋值给use_num_decimal ;

如果useNumDecimal 是JAVA中的BigDecimal类型,这里的判断在其值为0的情况下就会造成误判,此处的 item.useNumDecimal 为0,”“也在mybatis的语法分析中也是0,这就导致0==0 而0 != 0的条件不成立,所以不会赋值;
那么'' "为什么会转换成0呢,
就是下面的源码导致的。注意doubleValue方法中的 return s.length() == 0 ? 0.0D : Double.parseDouble(s);



    public static String stringValue(Object value, boolean trim) {
        String result;
        if (value == null) {
            result = OgnlRuntime.NULL_STRING;
        } else {
            result = value.toString();
            if (trim) {
                result = result.trim();
            }
        }

        return result;
    }


    public static double doubleValue(Object value) throws NumberFormatException {
        if (value == null) {
            return 0.0D;
        } else {
            Class c = value.getClass();
            if (c.getSuperclass() == Number.class) {
                return ((Number)value).doubleValue();
            } else if (c == Boolean.class) {
                return (Boolean)value ? 1.0D : 0.0D;
            } else if (c == Character.class) {
                return (double)(Character)value;
            } else {
                String s = stringValue(value, true);
                return s.length() == 0 ? 0.0D : Double.parseDouble(s);
            }
        }
    }

分析:
因为”“是String类型的且s.length() == 0成立,所以会转换成0.0D 也就是Doule类型的0,这也就是问题的根源;

处理方法只需要把这个and后面的判断去掉就好了也就是说把 and item.useNumDecimal != '' "去掉;

下面是Mybatis解析的一些思路及源码;
思路:

大致思路就是生成一个解析树,然后解析里面的值,看看条件是否成立,如果成立就把它生成到SQL语句中;

========================================================================

源码地址:

解析的入口在这里:

MappedStatement

  public BoundSql getBoundSql(Object parameterObject) {
    //获取SQL
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }

DynamicSqlSource
  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }



MixedSqlNode
  public boolean apply(DynamicContext context) {
    for (SqlNode sqlNode : contents) {
      sqlNode.apply(context);
    }
    return true;
  }

StaticTextSqlNode
  @Override
  public boolean apply(DynamicContext context) {
    context.appendSql(text);
    return true;
  }

DynamicContext
//如果返回的值为true,则把SQL加入到sqlBuilder 中
  private final StringBuilder sqlBuilder = new StringBuilder();
  public void appendSql(String sql) {
    sqlBuilder.append(sql);
    sqlBuilder.append(" ");
  }


SimpleNode
  //获取值
    public final Object getValue(OgnlContext context, Object source) throws OgnlException {
        Object result = null;
        if (context.getTraceEvaluations()) {
            EvaluationPool pool = OgnlRuntime.getEvaluationPool();
            Throwable evalException = null;
            Evaluation evaluation = pool.create(this, source);
            context.pushEvaluation(evaluation);
            boolean var13 = false;

            try {
                var13 = true;
                result = this.evaluateGetValueBody(context, source);
                var13 = false;
            } catch (OgnlException var14) {
                evalException = var14;
                throw var14;
            } catch (RuntimeException var15) {
                evalException = var15;
                throw var15;
            } finally {
                if (var13) {
                    Evaluation eval = context.popEvaluation();
                    eval.setResult(result);
                    if (evalException != null) {
                        eval.setException((Throwable)evalException);
                    }

                    if (evalException == null && context.getRootEvaluation() == null && !context.getKeepLastEvaluation()) {
                        pool.recycleAll(eval);
                    }

                }
            }

            Evaluation eval = context.popEvaluation();
            eval.setResult(result);
            if (evalException != null) {
                eval.setException((Throwable)evalException);
            }

            if (evalException == null && context.getRootEvaluation() == null && !context.getKeepLastEvaluation()) {
                pool.recycleAll(eval);
            }
        } else {
            result = this.evaluateGetValueBody(context, source);
        }

        return result;
    }



 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方法
        return this._hasConstantValue ? this._constantValue : this.getValueBody(context, source);
    }


ExpressionNode
    //判断常量的方法
    public boolean isConstant(OgnlContext context) throws OgnlException {
        boolean result = this.isNodeConstant(context);
        if (this._children != null && this._children.length > 0) {
            result = true;

            for(int i = 0; result && i < this._children.length; ++i) {
                if (this._children[i] instanceof SimpleNode) {
                    result = ((SimpleNode)this._children[i]).isConstant(context);
                } else {
                    result = false;
                }
            }
        }

        return result;
    }


ASTAnd
    //获取getValueBody的方法
    protected Object getValueBody(OgnlContext context, Object source) throws OgnlException {
        Object result = null;
        int last = this._children.length - 1;

        for(int i = 0; i <= last; ++i) {
            result = this._children[i].getValue(context, source);
            if (i != last && !OgnlOps.booleanValue(result)) {
                break;
            }
        }

        return result;
    }




OgnlRuntime
//如果不是常量最终会通过method.invoke获得其值
   public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException {
        boolean syncInvoke = false;
        boolean checkPermission = false;
        synchronized(method) {
            if (_methodAccessCache.get(method) == null) {
                if (Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
                    _methodAccessCache.put(method, Boolean.FALSE);
                } else if (!method.isAccessible()) {
                    _methodAccessCache.put(method, Boolean.TRUE);
                } else {
                    _methodAccessCache.put(method, Boolean.FALSE);
                }
            }

            if (_methodAccessCache.get(method) == Boolean.TRUE) {
                syncInvoke = true;
            }

            if (_methodPermCache.get(method) == null) {
                if (_securityManager != null) {
                    try {
                        _securityManager.checkPermission(getPermission(method));
                        _methodPermCache.put(method, Boolean.TRUE);
                    } catch (SecurityException var12) {
                        _methodPermCache.put(method, Boolean.FALSE);
                        throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
                    }
                } else {
                    _methodPermCache.put(method, Boolean.TRUE);
                }
            }

            if (_methodPermCache.get(method) == Boolean.FALSE) {
                checkPermission = true;
            }
        }

        Object result;
        if (syncInvoke) {
            synchronized(method) {
                if (checkPermission) {
                    try {
                        _securityManager.checkPermission(getPermission(method));
                    } catch (SecurityException var10) {
                        throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
                    }
                }

                method.setAccessible(true);
//在这里可以通过反射得到对应的值 这个案例中得到的值就是0
                result = method.invoke(target, argsArray);
                method.setAccessible(false);
            }
        } else {
            if (checkPermission) {
                try {
                    _securityManager.checkPermission(getPermission(method));
                } catch (SecurityException var9) {
                    throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
                }
            }

            result = method.invoke(target, argsArray);
        }

        return result;
    }

ASTNotEq
    protected Object getValueBody(OgnlContext context, Object source) throws OgnlException {
        Object v1 = this._children[0].getValue(context, source);//0
        Object v2 = this._children[1].getValue(context, source);//因为是常量所以值为""
        //因为是and条件,所以判断两个值是否相等
        return OgnlOps.equal(v1, v2) ? Boolean.FALSE : Boolean.TRUE;
    }


OgnlOps
//判断是否相等的方法
   public static boolean equal(Object v1, Object v2) {
        if (v1 == null) {
            return v2 == null;
        } 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()) {
            if (object2 != null && object2.getClass().isArray() && object2.getClass() == object1.getClass()) {
                result = Array.getLength(object1) == Array.getLength(object2);
                if (result) {
                    int i = 0;

                    for(int icount = Array.getLength(object1); result && i < icount; ++i) {
                        result = isEqual(Array.get(object1, i), Array.get(object2, i));
                    }
                }
            }
        } else {
            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 {
            int t1 = getNumericType(v1);
            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:
                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:
                double dv1 = doubleValue(v1);
                double dv2 = doubleValue(v2);

                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;
    }


    public static int getNumericType(Object value) {
        if (value != null) {
            Class c = value.getClass();
            if (c == Integer.class) {
                return 4;
            }

            if (c == Double.class) {
                return 8;
            }

            if (c == Boolean.class) {
                return 0;
            }

            if (c == Byte.class) {
                return 1;
            }

            if (c == Character.class) {
                return 2;
            }

            if (c == Short.class) {
                return 3;
            }

            if (c == Long.class) {
                return 5;
            }

            if (c == Float.class) {
                return 7;
            }

            if (c == BigInteger.class) {
                return 6;
            }

            if (c == BigDecimal.class) {
                return 9;
            }
        }

        return 10;
    }

    public static double doubleValue(Object value) throws NumberFormatException {
        if (value == null) {
            return 0.0D;
        } else {
            Class c = value.getClass();
            if (c.getSuperclass() == Number.class) {
                return ((Number)value).doubleValue();
            } else if (c == Boolean.class) {
                return (Boolean)value ? 1.0D : 0.0D;
            } else if (c == Character.class) {
                return (double)(Character)value;
            } else {
                String s = stringValue(value, true);

                return s.length() == 0 ? 0.0D : Double.parseDouble(s);
            }
        }
    }

    public static double doubleValue(Object value) throws NumberFormatException {
        if (value == null) {
            return 0.0D;
        } else {
            Class c = value.getClass();
            if (c.getSuperclass() == Number.class) {
                return ((Number)value).doubleValue();
            } else if (c == Boolean.class) {
                return (Boolean)value ? 1.0D : 0.0D;
            } else if (c == Character.class) {
                return (double)(Character)value;
            } else {
                String s = stringValue(value, true);
                  //在这里""->0
                return s.length() == 0 ? 0.0D : Double.parseDouble(s);
            }
        }
    }

如图 最终执行得到的结果是false;所以并不会进入赋值语句;

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容