关于Mybatis中if标签判断 != ‘‘ 的两个注意点

<select id="getUserList" parameterType="map" resultType="map">
        select a.*
        from user a
        where 1 = 1
        <if test="age != null and age != ''">
            and a.age = #{age}
        </if>
        <if test="createtime != null and createtime != ''">
            and a.createtime <![CDATA[ <= ]]> #{createtime}
        </if>
</select>

对于上述 SQL 代码段,首先声明这段 SQL 是存在异常的,第一个 if 是判断传入的 age(int) 是否为空,不为空则拼接上过滤条件;第二个 if 是判断传入的 createtime(String) 是否为空,不为空则拼接上过滤条件。
1.对于第一个 if 判断,即对整形的判断,如果 age 不为 0,则不会存在问题。但是现在如果有一个需求,想要获取 age = 0 的用户列表,那么当传入 age = 0 时,查出来的将会是所有的数据。这是因为当你传入 age = 0 时,Mybatis 会将你传入的值和条件语句进行判断,而条件中的 age != ‘’ 的 ‘’ 会被解析成 0.0,所以相当于条件就变为了 age != 0,而你传入的 age 又是 = 0 的,所以等同于 0 != 0,那么这个条件返回值为 false,所以判断条件就变为了如下所示:

<if test="0 != null and 0 != 0">
     and a.age = #{age}
</if>

这样,条件无法成立,永远也获取不到 age = 0 的数据。可以查看 Mybatis 源码,关键源码如下:

// OgnlOps.class 文件中的 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);
                // 这里就是将 age != '' 中的 '' 解析为 0.0 的地方
                return s.length() == 0 ? 0.0D : Double.parseDouble(s);
            }
        }
    }
// ASTNotEq.class 文件中的 getValueBody 方法
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);
        // 这里是判断条件最终是否成立的地方,返回结果为 false
        return OgnlOps.equal(v1, v2) ? Boolean.FALSE : Boolean.TRUE;
    }

有一篇文章对于源码说的比较清楚,供参考:
Mybatis if判断Integer类型的值不等于’‘引发的问题(!=’'等价于!=0)
2.对于第二个 if 判断,如果传入的 createtime 为 Date 类型,则会抛出如下异常:
在这里插入图片描述
这里可以清楚的看到是因为类型不同无法进行比较抛出的异常,源码如下:

// OgnlOps.class 文件中的 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;
    }

3.修改后的 SQL 如下:只需要将 != ‘’ 删除即可

<select id="getUserList" parameterType="map" resultType="map">
        select a.*
        from user a
        where 1 = 1
        <if test="age != null">
            and a.age = #{age}
        </if>
        <if test="createtime != null">
            and a.createtime <![CDATA[ <= ]]> #{createtime}
        </if>
</select>

你可能感兴趣的:(Mybatis,java,mysql)