mybatis 空字符串和0

时间:2022-02-23 16:16:22

最近在使用Mybatis的过程中遇到了一个奇怪的问题,如下所示:查询SQL中的一个状态条件,在param.sendstate=0或10时,单独处理. 

<choose>
    <when test="param.sendstate!=null and param.sendstate == 10">
        and (SendState = 2 and (RESPONSETYPETEXT is null or RESPONSETYPETEXT =''))
    </when>
    <when test="param.sendstate!=null and param.sendstate == 0">
        and (SendState = 0 or SendState = '' or SendState is null)
    </when>
    <otherwise>
        <if test="param.sendstate!=null and param.sendstate!=''">
            and SendState = #{param.sendstate}
        </if>
    </otherwise>
</choose>

但是,当param.sendstate为空字符串时,通过控制台打印的SQL发现执行的SQL是:

mybatis 空字符串和0

也就是说此时 param.sendstate == 0结果是true

这个问题困扰了我半天,最后改成如下形式解决

<if test="param.state!=null and param.state!=''">
    <choose>
        <when test="param.state == 10">
            and (state = 10 and responsetext is null)
        </when>
        <when test="param.state == 0">
            and (state = 0 or state is null)
        </when>
        <otherwise>
            and state = #{param.state}
        </otherwise>
    </choose>
</if>

由于时间紧急,没有深入去研究这个问题.后来看到了这篇文章

Mybatis参数Integer类型值为0 源码处理

http://www.cnblogs.com/gushen-super/archive/2018/06/28/9238161.html

简单跟踪了下代码,明白了问题在哪.

Mybatis在处理 if 标签时,使用OGNL表达式处理并返回表达式的结果.

条件:param.sendstate == 0,此时param.sendstate是空字符串

OGNL在处理 == 运算时,最终调用的是 OgnlOps类的compareWithConversion方法

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

compareWithConversion方法有两个Object类型的参数v1和v2,带入上面的条件,即

  v1 = param.sendstate(空字符串)

  v2 = 0

经过getNumericType方法的处理,最终进入case=8里面处理,doubleValue方法如下:

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

可以看到,当value = 0 的时候,结果值为0.0,当value为空字符串时,结果值为0.0D

所以dv1 == dv2 结果为true,compareWithConversion方法的最终返回值为0,即相等. 

mybatis 空字符串和0