Mybatis if判断Integer类型的值不等于''引发的问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_30038111/article/details/82665763

案例

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

<update id="update" parameterType="com.p7.demo.model.Person">
    update person
    <set>
        <if test="age != ''">
            age = #{age}
        </if>
    </set>
    where id = 1
</update>

为什么 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的情况,即<if test="age != '' or age == 0">

源码分析

mybatis调用OGNL源码如下:

  1. age != ''表达式封装为ognl.Nodeognl.Node使用组合设计模式,Node分为普通节点,常量节点等类型的节点
  2. 调用OgnlgetValue静态方法,参数1:条件表达式封装的ognl.Node,参数2:Person对象(mybatis传入的是一个Map

    这里写图片描述

测试OGNL的代码:

依赖:

<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
    <version>2.6.9</version>
</dependency>

代码:

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);
}

OGNL源码如下:

  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,即此类节点一般没有子节点

    这里写图片描述

    这里写图片描述

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

    这里写图片描述

    这里写图片描述

  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做判断的值''
    这里写图片描述

    这里写图片描述

    这里写图片描述

  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,最终<if test="age != ''">返回false,条件不成立

    这里写图片描述

    这里写图片描述

    这里写图片描述

    这里写图片描述

    这里写图片描述

    这里写图片描述

    这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_30038111/article/details/82665763