问题
<if test="field!=null and field!=''">
and field=#{field}
</if>
mybatis中经常会用这种写法,这种写法一般都说,field是String类型.如果field在接口层是一个Integer(之所以是Integer,是历史代码不规范),并且是0的话,这个if条件是true 还是 false呢?
咋一看,感觉应该是true,因为0!=null && 0!=’’,但是实际上这个是false.这个场景下,mybatis认为空字符串和0是相等.这个结论很难以相信,是mybatis刻意为之,还是代码的bug?debug了下mybatis源码,找到了问题根源:
源码
mybatis源码->org.apache.ibatis.ognl.ASTEq->getValueBody->OgnlOps.equal(v1, v2)->OgnlOps.isEqual->compareWithConversion
result = compareWithConversion(object1, object2, true) == 0 || object1.equals(object2)
// compareWithConversion方法通过如下代码去比较
case 8:
double dv1 = doubleValue(v1);
double dv2 = doubleValue(v2);
return dv1 == dv2 ? 0 : (dv1 < dv2 ? -1 : 1);
// 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 (double)((Boolean)value ? 1 : 0);
} else if (c == Character.class) {
return (double)(Character)value;
} else {
// Integer的话,会走这个elase分支
String s = stringValue(value, true);
// 空字符串的长度是0,所以空字符串被转换成0.0D
return s.length() == 0 ? 0.0D : Double.parseDouble(s);
}
}
}
结论
关键在于 return s.length() == 0 ? 0.0D : Double.parseDouble(s); 这行代码,空字符串被转成了0.0D,0也被转成了0.0D,导致( 0 == ‘’ )为true.
1.对于Integer类型的字段如果加上field!=’’的判断,会不知不觉的导致0判断失败,导致field字段无法参与where过滤;
2.如果field是insert的字段,if判断失败,会导致field==0的时候,field字段无法插入。
解决办法:
1.将field字段改为String类型
2.去掉field!=’‘的判断,值得注意的是,现在主流的自动生成mybatis的工具,都会对Integer字段加上这个field!=’’的判断