mybatis if test踩坑记录

探讨MyBatis中ifTest标签在处理Integer、Long等非字符串类型参数时的判断逻辑,解析为何0会被误判为true,及1=''为false的现象,深入OgnlOps.compareWithConversion方法,理解不同数据类型的比较机制。

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

背景

使用mybatis进行sql查询时,当存在可选参数时,难免会使用的if标签进行判断参数是否存在,如果存在则当做参数拼接到sql中进行查询,否则丢弃
复制代码

现象

当截图中代码是Integer,Long类型时,传入age=0,框中代码输出结果为false;age=1时,为true;

解决方案

当入参为Long,Integer等非字符串类型时,if test不能使用param!='' (理解一下就是数字类型不能跟字符串类型进行对比)
复制代码

引发疑问

· if test 为什么会判断0=‘' true
· if test 中怎么计算的1='' 为false
复制代码

个人理解

''理解为空字符串,空字符在getNumericType方法中匹配的值为数字10,1在入参为Integer类型,
在getNumericType方法中匹配的是数字4;两个参数在compareWithConversion方法中执行case8;

最后''在doubleValue方法中被转换为0.0D, 所以表达式0=''返回true。
复制代码

疑问备注

首先IfSqlNode会从xml文件中获取if标签,提取test表达式,并对表达式进行计算;
复制代码

具体计算部分源码: 入口ASTNotEq.class:

// 获取等号两边的值进行比较
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.class
// 首先判断入参是否为空,然后判断判断是否相等,进入isEqual方法
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;
    }
}


// 先判断是否为空,然后判断入参是否为array类型,最后进行判空和转换比较,进入compareWithConversion方法;
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;
}


// 如果两个入参不相等,则通过getNumericType方法将入参转为响应的int值,最后得到响应的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);
        }
    }
}
复制代码

转载于:https://juejin.im/post/5c74b0c86fb9a049ee811ae7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值