在一次项目中,执行一次修改状态的SQL,发现修改状态为0时总是修改不成功,跟踪代码发现以下内容
关键因素
1.属性为Int类型,且值为0
2.mybatis动态sql中使用了<if test="param != null and param !=''">
原因分析
问题主要出在 DynamicSqlSource 这个类中,对sql的拼接
发现在这一步返回的sql就已经缺失了我想要的param
SqlNode.apply()有很多实现方法
在mybatis启动初始化SqlSessionFactoryBean的时候就对xml文件中的sql做了结构化的编译
比如我的一个sql被处理成SqlNode对象时是这样的
所以定位本文问题的话,我们重点关注 IfSqlNode
public boolean apply(DynamicContext context) {
//这里对表达式做计算
if (this.evaluator.evaluateBoolean(this.test, context.getBindings())) {
this.contents.apply(context);
return true;
} else {
return false;
}
}
通过调用了 SimpleNode.getValue
它的实现类根据不同表达式分为一系列的 ASTXXXX的实现类
我使用类!= 根据类名就可以确定到是 ASTNotEq
class ASTNotEq extends ComparisonExpression {
public ASTNotEq(int id) {
super(id);
}
public ASTNotEq(OgnlParser p, int id) {
super(p, id);
}
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;
}
public String getExpressionOperator(int index) {
return "!=";
}
public String getComparisonFunction() {
return "!ognl.OgnlOps.equal";
}
}
进入到 OgnlOps.isEqual当表达式为 0 != "" 可以判断会进入
result = compareWithConversion(object1, object2) == 0
public static boolean isEqual(Object object1, Object object2) {
boolean result = false;
if (object1 == object2) {
result = true;
} else if (object1 != null && object2 != null) {
int i;
int icount;
if (object1.getClass().isArray()) {
if (object2.getClass().isArray() && object2.getClass() == object1.getClass()) {
result = Array.getLength(object1) == Array.getLength(object2);
if (result) {
i = 0;
for(icount = Array.getLength(object1); result && i < icount; ++i) {
result = isEqual(Array.get(object1, i), Array.get(object2, i));
}
}
}
} else {
i = getNumericType(object1);
icount = getNumericType(object2);
if (i != 10 || icount != 10 || object1 instanceof Comparable && object2 instanceof Comparable) {
result = compareWithConversion(object1, object2) == 0;
} else {
result = object1.equals(object2);
}
}
}
return result;
}
根据我们的入参数,会继续调用 OgnlOps.getNumericType(Object value),这个方法也很通俗易懂
参数为0返回的是4,参数为“”返回了10
然后调用 getNumericType( 4, 10, true)
所以结果为10
在OgnlOps.compareWithConversion中 结果为10,且 t1或t2为10 那么就会返回标签 label75
case 10:
if (t1 != 10 || t2 != 10) {
break label75;
}
if (v1 instanceof Comparable && v1.getClass().isAssignableFrom(v2.getClass())) {
result = ((Comparable)v1).compareTo(v2);
break;
}
if (!(v1 instanceof Enum) || !(v2 instanceof Enum) || v1.getClass() != v2.getClass() && ((Enum)v1).getDeclaringClass() != ((Enum)v2).getDeclaringClass()) {
throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and " + v2.getClass().getName());
}
result = ((Enum)v1).compareTo(v2);
break;
那么两个参数都会被转为double数值,最终都为 0.0D
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);
}
}
}
及 compareWithConversion(0,"")将返回true
所以 ASTNotEq将返回false
最终在 IfSqlNode中将此表达式过滤了
结论
int类型在动态sql中不应该使用 param !=''" 的判断表达式