Mybatis 關(guān)于Bigdecimal類型 動(dòng)態(tài)SQL != "" 導(dǎo)致不能更新(賦值)的問(wèn)題,mybatis的動(dòng)態(tài)SQL中""會(huì)被解析成0

太長(zhǎng)不看系列:在mybatis的動(dòng)態(tài)SQL中""會(huì)被解析成0;

在mybtais中我們一般會(huì)用動(dòng)態(tài)SQL處理自己的語(yǔ)句,也就是可以通過(guò)if語(yǔ)句判斷是否成立,然后在進(jìn)行賦值操作,
比如:

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

這里的意思就是判斷useNumDecimal不是null并且也不等于 '' ",然后就把useNumDecimal的值賦值給use_num_decimal ;

如果useNumDecimal 是JAVA中的BigDecimal類型,這里的判斷在其值為0的情況下就會(huì)造成誤判,此處的 item.useNumDecimal 為0,”“也在mybatis的語(yǔ)法分析中也是0,這就導(dǎo)致0==0 而0 != 0的條件不成立,所以不會(huì)賦值;
那么'' "為什么會(huì)轉(zhuǎn)換成0呢,
就是下面的源碼導(dǎo)致的。注意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);
            }
        }
    }

分析:
因?yàn)椤薄笆荢tring類型的且s.length() == 0成立,所以會(huì)轉(zhuǎn)換成0.0D 也就是Doule類型的0,這也就是問(wèn)題的根源;

處理方法只需要把這個(gè)and后面的判斷去掉就好了也就是說(shuō)把 and item.useNumDecimal != '' "去掉;

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

大致思路就是生成一個(gè)解析樹,然后解析里面的值,看看條件是否成立,如果成立就把它生成到SQL語(yǔ)句中;

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

源碼地址:

解析的入口在這里:

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;
        }
        //不是常量就調(diào)用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
//如果不是常量最終會(huì)通過(guò)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);
//在這里可以通過(guò)反射得到對(duì)應(yīng)的值 這個(gè)案例中得到的值就是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);//因?yàn)槭浅A克灾禐?"
        //因?yàn)槭莂nd條件,所以判斷兩個(gè)值是否相等
        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;
    }

//最終來(lái)到了這里
    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);
            }
        }
    }

如圖 最終執(zhí)行得到的結(jié)果是false;所以并不會(huì)進(jìn)入賦值語(yǔ)句;

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容