Mybatis异常之Caused by: java.lang.NumberFormatException: For input string: xxx

本文分析了一个Mybatis使用OGNL表达式时的常见错误,详细解释了当字符串转换为数字进行比较时引发的NumberFormatException异常。通过源码解读,展示了异常产生的具体过程,帮助开发者理解并避免此类问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mybatis异常

前言

大家先看看下面这个xml有什么问题

<select id="getUserByName" resultType="User">
    select * from user where 1=1
    <if test="name != 0">
        and name= #{name}
    </if>
</select>

如果你看出来了,好了,你可以出去看看自己的test内容是不是写错了,test=”name != 0”>,字符串转换后仍然不能和数字进行比较,从而报错。

异常堆栈信息

Caused by: java.lang.NumberFormatException: For input string: "xxx"
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
    at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    at java.lang.Double.parseDouble(Double.java:538)
    at org.apache.ibatis.ognl.OgnlOps.doubleValue(OgnlOps.java:243)
    at org.apache.ibatis.ognl.OgnlOps.compareWithConversion(OgnlOps.java:100)
    at org.apache.ibatis.ognl.OgnlOps.isEqual(OgnlOps.java:143)
    at org.apache.ibatis.ognl.OgnlOps.equal(OgnlOps.java:802)
    at org.apache.ibatis.ognl.ASTNotEq.getValueBody(ASTNotEq.java:53)
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
    at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:470)
    at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:434)
    at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:44)
    at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:32)
    at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:34)
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33)
    at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:41)
    at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:292)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
    ... 6 more

通过堆栈信息,我们可以看到是mybatis中的Ognl处理中,转换两边内容进行判定时出现异常,Double.parseDouble(s)转化失败

以下是部分源码可以体现Ognl的处理:
ASTNotEq中部分源码:
调用OgnlOps.equal(v1, v2),true返回false,false返回true;

protected Object getValueBody(OgnlContext context, Object source) throws OgnlException {
    Object v1 = this._children[0].getValue(context, source);
    Object v2 = this._children[1].getValue(context, source);
    return OgnlOps.equal(v1, v2) ? Boolean.FALSE : Boolean.TRUE;
}

OgnlOps中部分源码:

  • 两者都为null返回true;两者==或者isEqual(v1, v2)返回true;两者都instanceof
    Number,并且值相同返回true;其他返回false;
  • isEqual中先判断两者==和是否都数组,长度以及内容是否相同
  • 最后通过getNumericType转化两者到一个类型进行对比
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 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 int getNumericType(int t1, int t2, boolean canBeNonNumeric) {
    if (t1 == t2) {
        return t1;
    } else if (canBeNonNumeric && (t1 == 10 || t2 == 10 || t1 == 2 || t2 == 2)) {
        return 10;
    } else {
        if (t1 == 10) {
            t1 = 8;
        }

        if (t2 == 10) {
            t2 = 8;
        }

        if (t1 >= 7) {
            if (t2 >= 7) {
                return Math.max(t1, t2);
            } else if (t2 < 4) {
                return t1;
            } else {
                return t2 == 6 ? 9 : Math.max(8, t1);
            }
        } else if (t2 >= 7) {
            if (t1 < 4) {
                return t2;
            } else {
                return t1 == 6 ? 9 : Math.max(8, t2);
            }
        } else {
            return Math.max(t1, t2);
        }
    }
}
//...
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 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);
        }
    }
}

结论

这类问题如果堆栈中打印的有ognl表达式相关的,直接先看test中内容,然后看#{}或者${}中是不是有一些ognl表达式没有处理好。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值