JAVA-MyBatis 当传入参数为Integer的0时,动态sql解析为空

JAVA-MyBatis 当传入参数为Integer的0时,动态sql解析为空

之前开发时遇到前台传来参数为0 时,使用mybatis拼接动态sql时将0解析为空从而跳过if判断参数是否为空,无法拼接限制条件的问题,现在深入研究问题的原因

  • 现象:

    当mybatis的xml文件中使用动态sql的if判段,如下:

    <if test="warningId !=null and warningId !=''">
    			and l.warning_id = #{warningId}
    </if>
    

    warningId为Integer的0时,if判断会为false

  • 表面原因:

    mybatis会将0解析为 ‘’ 空 warningId!=’’ 为false跳过拼接条件

  • 解决方法:

    前台处理:将传入参数改为字符串的0

    后台处理:判断条件加上等于0的时候,如

    <if test="warningId !=null and warningId !='' or warningId == 0 " >
    			and l.warning_id = #{warningId}
    </if>
    
  • 原理及源码:

    查询资料以及查看mybatis源码,搞清楚mybatis解析xml文件时对if元素的处理

    1. 当mybatis遍历节点时,遇到节点类型为元素,会使用NodeHandler处理各个类型的元素,NodeHandler是XMLScriptBuilder的一个内部接口,其实现类包括TrimHandler、WhereHandler、SetHandler、IfHandler、ChooseHandler,处理后,会生成IfSqlNode

      private class IfHandler implements NodeHandler {
          public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            List<SqlNode> contents = parseDynamicTags(nodeToHandle);
            MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
            String test = nodeToHandle.getStringAttribute("test");
            IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
            targetContents.add(ifSqlNode);
          }
      }
      
    2. 当处理完所有节点后,开始构造sql

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
          DynamicContext context = new DynamicContext(configuration, parameterObject);
          rootSqlNode.apply(context);//此处调用sqlnode的apply方法
          SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
          Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
          SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
          BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
          context.getBindings().forEach(boundSql::setAdditionalParameter);
          return boundSql;
        }
      

      rootSqlNode.apply(context);该处调用sql的apply方法,然后查看IfSqlNode的结构:

      public class IfSqlNode implements SqlNode {
        private final ExpressionEvaluator evaluator;
        private final String test;
        private final SqlNode contents;
      
        public IfSqlNode(SqlNode contents, String test) {
          this.test = test;
          this.contents = contents;
          this.evaluator = new ExpressionEvaluator();
        }
      
        @Override
        public boolean apply(DynamicContext context) {
          if (evaluator.evaluateBoolean(test, context.getBindings())) {
            contents.apply(context);
            return true;
          }
          return false;
        }
      
      }
      

      其中appl方法中使用evaluator.evaluateBoolean方法对表达式进行判断,evaluator是一个ExpressionEvaluator 类型,它解析表达式使用的是OGNL,对象导航图语言(Object Graph Navigation Language),简称OGNL,是应用于Java中的一个开源的表达式语言.

      public class ExpressionEvaluator {  
        public boolean evaluateBoolean(String expression, Object parameterObject) {  
          Object value = OgnlCache.getValue(expression, parameterObject);  
          if (value instanceof Boolean) return (Boolean) value;  
          if (value instanceof Number) return !new BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO);  
          return value != null;  
        }  
      
      

      查询OGNL的官方文档,

      Interpreting Objects as Booleans

      Any object can be used where a boolean is required. OGNL interprets objects as booleans like this:

      • If the object is a Boolean, its value is extracted and returned;

      • If the object is a Number, its double-precision floating-point value is compared with zero; non-zero is treated as true, zero as false;

      • If the object is a Character, its boolean value is true if and only if its char value is non-zero;

      • Otherwise, its boolean value is true if and only if it is non-null.

        其中说明,如果对象是一个Number的类型,值为0时将被解析为false,否则为true,即’’ == 0 == false

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值