Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)

案例

当传入的person属性age的值为0时,mybatis预编译下面的语句会报错,因为预编译的sql为:update person where id = 1


   	update person
   	
   		
   			age = #{age}
   		
   	
   	where id = 1

为什么 if 条件判断没有通过?

通过源码了解到,mybatis在预编译sql时,使用OGNL表达式来解析if标签,对于Integer类型属性,在判断不等于''时,例如age != ''OGNL会返回''的长度,源码:(s.length() == 0) ? 0.0 : Double.parseDouble( s ),因此表达式age != ''被当做age != 0来判断,所以当age0时,if条件判断不通过。

总结

  1. 在设计实体类时,实体属性要使用基本数据类型的包装类型,因为基本数据类型有默认值
  2. if条件判断number类型,没必要判断''的情况,只需判断null的情况,如果非要判断''的情况,那么要考虑到等于0的情况,即

源码分析

mybatis调用OGNL源码的入口:

  1. age != ''表达式封装为ognl.Nodeognl.Node使用组合设计模式,Node分为普通节点,常量节点等类型的节点

  2. 调用OgnlgetValue静态方法,参数1:条件表达式封装的ognl.Node,参数2:Person对象(mybatis传入的是一个Map

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第1张图片

调试OGNL的代码:

依赖:


    ognl
    ognl
    2.6.9

代码:

public static void main(String[] args) throws Exception {
	String expression = "age != ''";
	Node node = new OgnlParser(new StringReader(expression)).topLevelExpression();
	Person person = new Person();
	person.setAge(0);
	Object value = Ognl.getValue(node, person);
	System.out.println(value);
}
  1. 条件表达式判断入口,Ognl#getValue方法

    这里写图片描述

  2. Ognl#getValue重载方法,这个方法将参数tree强转为Node类型,它的实际类型是SimleNode,并调用SimleNode#getValue方法,SimleNode中有Node[] children属性,OGNL使用组合设计模式,将age != ''分成ASTProperty封装的age属性节点和ASTConst封装的''常量节点,且age属性节点的children属性有一个ASTConst常量节点,它valueage的属性名称。ASTConst类型节点的children一般情况下为null,即此类节点一般没有子节点

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第2张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第3张图片

  3. SimleNode#getValue方法else分支调用了evaluateGetValueBody方法,此方法中调用ExpressionNode#isConstant方法,判断SimleNodechildren是否全是常量节点(常量节点请参考SimleNode的子类),这里hasContantValuefalse,所以在执行return的三目运算时,调用ASTNotEq#getValueBody方法,因为我们的条件是不等于,所以我们应该查看ASTNotEq中的方法。

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第4张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第5张图片

  4. ASTNotEq#getValueBody方法,SimpleNode中的children[0]age属性节点,children[1]''常量节点,这里分别获取节点的值。children[0]是属性节点,调用getValue方法,重复走第3步的操作,注意,此时调用的getValueBodyASTProperty的方法,获取age节点的常量节点,并从person类中提取age的值,而children[1]是常量节点,因此走第3步操作时,hasConstantValuetrue,直接返回constantValue,值为""。然后调用OgnlOps#equal方法,参数1:age的实际值,参数2:与age做判断的值''
    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第6张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第7张图片

    这里写图片描述

  5. OgnlOps#equal方法,在第二个if条件中,调用isEqual方法,isEqual最里层的else中调用的compareWithConversion方法,首先通过getNumericType方法获取到参数的真实类型,再通过getNumericType重载方法获取两个参数的最大类型(参见NumericTypes接口)这里返回NONNUMERIC,然后通过switch分支处理,由于参数1和参数2不都为NONNUMERIC,因此switch分支走DOUBLEcase,分别获取两个参数的Double值,对于参数2空字符串,在调用doubleValue方法时,先去掉左右空格,再取字符串长度作为最后的值,与age的值进行比较,即return (dv1 == dv2) ? 0 : ((dv1 < dv2) ? -1 : 1);dv1dv2都是0,然后回看result = (compareWithConversion(object1, object2, true) == 0) || object1.equals(object2);返回truereturn OgnlOps.equal( v1, v2 )? Boolean.FALSE : Boolean.TRUE;返回false,最终返回false,条件不成立

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第8张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第9张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第10张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第11张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第12张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第13张图片

    Mybatis if判断Integer类型的值不等于''引发的问题(!=''等价于!=0)_第14张图片

你可能感兴趣的:(mybatis)