zoukankan      html  css  js  c++  java
  • mybatis if test 采坑记

    前言

    最近遇到的一个关于mybatis的xml条件判断的问题,平时写的都是以判断空属性是否为空,例如:

    
    <if test="type!=null and type !=''">  
        and status = 1   
    </if>
    
    

    所以在这一块并没有足够的重视。

    正文

    最近刚好要写一个与上边不一样的写法,但是还是以上面的为基础,是对属性的值进行判断:
    判断type属性的值

    
    <if test="type!=null and type !=''">  
        <if test="type=='6'">  
            and status = 61   
        </if>
        <if test="type!='6'">  
            and status = 2  
        </if>
    </if>
    
    

    结果就是无论我传什么值,SQL中都是执行的status=2。
    在网上也找到了很多解决办法,但是都不是自己想要的结果;

    网上的解决办法

    1. 内双外单
    
    <if test='type=="6"'>  
        and status = 1   
    </if>
    
    
    1. 加.toString()
    
    <if test="type=='6'.toString()">  
        and status = 1   
    </if>
    
    

    源码分析

    从参考文章1中可以知道,mybatis使用ognl表达式来进行解析的;下面使用ognl来模拟解析;且不使用上面的两种方法;

    pom引入

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

    测试代码:

    
           // 构建一个OgnlContext对象
           OgnlContext context = (OgnlContext) Ognl.createDefaultContext(this,
                   new DefaultClassResolver(),
                   new DefaultTypeConverter(),
                   new DefaultMemberAccess(true));
    
           Map<String,Object> hashMap=new HashMap<>(1);
           hashMap.put("name", "aa");
           context.setRoot(hashMap);
           String expression="name =='aa'";
           try {
               Boolean flag = (Boolean) Ognl.getValue(expression, context, context.getRoot());
               System.out.println(flag);
           } catch (OgnlException e) {
               e.printStackTrace();
           }
    
    

    自己的解决办法

    除了网上的那两种办法,还有其他的一些方法可以解决我遇到的问题;

    1. 由于我比较的是数字类型,将传入的值改为数字类型的值传入,再将mybatis的xml中的单引号去掉,如下:
            
           hashMap.put("name", 6);
           context.setRoot(hashMap);
           String expression="name ==6";
    

    ognl源码解析

    下面对ognl的源码进行分析:

    1. 根据Ognl.getValue方法定位到 OgnlOps类的isEqual方法

        
        public static boolean isEqual(Object object1, Object object2)
        {
            boolean result = false;
    // 1. 判断两个对象是否为同一个对象
            if (object1 == object2) {
                result = true;
            } else {
    // 2. 集合判断
                if ((object1 != null) && object1.getClass().isArray()) {
                    if ((object2 != null) && object2.getClass().isArray() && (object2.getClass() == object1.getClass())) {
                        result = (Array.getLength(object1) == Array.getLength(object2));
                        if (result) {
                            for(int i = 0, icount = Array.getLength(object1); result && (i < icount); i++) {
                                result = isEqual(Array.get(object1, i), Array.get(object2, i));
                            }
                        }
                    }
                } else {
    // 3. 非集合判断
    // 3.1 当两个值都为String类型时,就直接equals后就返回了,如果不是,则进入compareWithConversion方法;
                    // Check for converted equivalence first, then equals() equivalence 
                    result = (object1 != null) && (object2 != null)&& (object1.equals(object2) || (compareWithConversion(object1, object2) == 0));
                }
            }
            return result;
        }
    
    

    这个方法传入了两个值,第一个值是调用mybatis方法传入的值,这个值的类型就有很多种了,例如BigDecimal,String,Integer等等,
    第二个是从mybatis的xml中读取出来的值,这个值要根据xml文件中的写法来判断了:

    1. 外双内单
      1.1 单长度为1时读取为Character,
      1.2 单长度大于1时,读取为String

    再根据第三个判断定位到compareWithConversion方法

    2. compareWithConversion方法

    1. 该方法以getNumericType方法来判断参数数字类型,其实可以根据方法名判断出,进入到这里后,都是做数字类型判断,再转换,再比较
    2. 在转换类型的过程中,如果传入的字符无法被转换,将抛出错误,例如:传入的值为字母就无法被转换;
    3. 当mybatis的xml文件中的判断值为""或者''时,该方法会将值赋值为0;
    4. 当mybatis的xml文件中的判断值为'',且里面的值为单数值时,注意转换的值并不是原值;例如:'6'解析为double:54.0
    
    public static int compareWithConversion(Object v1, Object v2)
        {
            int result;
            if (v1 == v2) {
                result = 0;
            } else {
                // 1
                int t1 = getNumericType(v1), t2 = getNumericType(v2), type = getNumericType(t1, t2, true);
                switch(type) {
                case BIGINT:
                    result = bigIntValue(v1).compareTo(bigIntValue(v2));
                    break;
                case BIGDEC:
                    result = bigDecValue(v1).compareTo(bigDecValue(v2));
                    break;
                case NONNUMERIC:
                    if ((t1 == NONNUMERIC) && (t2 == NONNUMERIC)) {
                        if ((v1 instanceof Comparable) && v1.getClass().isAssignableFrom(v2.getClass())) {
                            result = ((Comparable) v1).compareTo(v2);
                            break;
                        } else {
                            throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and "
                                    + v2.getClass().getName());
                        }
                    }
                    // else fall through
                case FLOAT:
                case DOUBLE:
                    // 这里注意doubleValue方法,当传入的值为字母时,会抛出错误;
                    double dv1 = doubleValue(v1),
                    dv2 = doubleValue(v2);
                    return (dv1 == dv2) ? 0 : ((dv1 < dv2) ? -1 : 1);
                default:
                    long lv1 = longValue(v1),
                    lv2 = longValue(v2);
                    return (lv1 == lv2) ? 0 : ((lv1 < lv2) ? -1 : 1);
                }
            }
            return result;
        }
    
    

    总结

    1. 如果属性不为数字类型,使用上边的网上的解决办法就差不多可以了

    2. 如果属性为数字类型,例如Integer,BigDecimal等等会有一个问题
      当传入的值为0时,且当mybatis的xml文件中的比较为''或者""时,比较的结果就是false,如下:

    
        // 1 双引号"",传入的值为0,或者其他类型为0的值
        hashMap.put("name",0); 
        hashMap.put("name",BigDecimal.ZERO);
        String expression="name!=null and name!=""";
    
        // 2 单引号,传入的值为0,或者其他类型为0的值
        hashMap.put("name",0);
        hashMap.put("name",BigDecimal.ZERO);
        String expression="name!=null and name!=''";
    
    

    解决办法:

    1. 将传入的值类型转换为String类型
    2. 在mybatis的xml中修改,如下:
    
        <if test="infSrc != null and infSrc!=''">AND INF_SRC = #{infSrc,jdbcType=BIGINT}</if>
        // 或者
        <if test="infSrc != null and infSrc!=''">AND INF_SRC = #{infSrc,jdbcType=DECIMAL}</if>
    
    

    这个根据自己传入值类型而定;

    最后

    项目地址:https://github.com/guodayede/java-study/tree/master/ognl

    参考文章

    1. mybatis的if test 字符串的坑
    2. Mybatis if test 字符串比较不生效
    3. MyBatis中OGNL表达式的强制对象类型
  • 相关阅读:
    python基础一 day41 IO模型 非阻塞IO
    python基础一 day41 协程
    python基础一 day41 复习
    python基础一 day40 条件 定时器 队列 线程池
    python基础一 day40 线程锁 信号量 事件
    第三章
    第二章
    第一章
    计算机基础知识
    scrapy安装方法
  • 原文地址:https://www.cnblogs.com/guoyuchuan/p/14106086.html
Copyright © 2011-2022 走看看