最近在使用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是:
也就是说此时 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,即相等.