【<if test="takeWay == '0'">】mybatis的if判断

单个的字符要写到双引号里面才行,改为<if test='takeWay == "1"'>或者改为<if test="takeWay == '1'.toString() ">

.xml文件的部分代码

<insert id="insertDelivery" parameterType="com.zuci.request.DeliveryPreferenceReq">     
insert cx_customer_deliverypreference     
<trim prefix="(" suffix=")" suffixOverrides=",">           
.... 此处省略       
<if test="takeWay == '1' and workday != null ">         
WORKDAY,       
</if>       
....     
</trim>
     
<trim prefix="values (" suffix=")" suffixOverrides=",">         
.... 此处省略         
<if test="takeWay == '1' and workday != null ">           
#{workday, jdbcType=VARCHAR},     
</if>     
....   
</trim>
</insert>

takeWay == “1”处出错,导致不执行if判断中的sql,运行程序不报错,没有任何提示。去掉takeWay == “1” and 则可执行。对此我百思不得其解,

因为自己有写过如下代码,是没错的。

<if test="messageType == 'senderReceiveSuccess' ">
......
</if>

把<if test="takeWay == '1' and workday != null ">

改为<if test='takeWay == "1" and workday != null '>

或改为<if test="takeWay == '1'.toString() and workday != null ">即可。

原因是:mybatis是用OGNL表达式来解析的,在OGNL的表达式中,’1’会被解析成字符,java是强类型的,char 和 一个string 会导致不等,所以if标签中的sql不会被解析。

总结下使用方法:单个的字符要写到双引号里面或者使用.toString()才行!

使用Mybatis时,常常会判断属性是否为空

POJO

private Integer status;//状态,可能为0、1、2、3。

Mapper XML

<sql>
<trim prefix="where" prefixOverrides="and | or ">
//...省略其他
<if test="status != null and status !=''">and status = #{status}</if>
<trim prefix="where" prefixOverrides="and | or ">
</sql>

status的值为 0时该where SQL and status = 0并未正常拼接,也就是说test内的表达式为false,从而导致查询结果错误。但是,显然该值(Integer :0)!= null也!= ' ',应该为true才对。

当status为Integer类型,并且status值为0时,该if判断却为false。

当status为0时,Mybatis会解析成'' 空字符串。

为了避免这个问题,改成下面这样写,去掉对空字符的判断,就解决了该问题

<if test="status != null">and status = #{status}</if> 

原因分析

通过Debug MyBatis源码顺藤摸瓜找到了IfSqlNode类,该类用来处理动态SQL的<if>节点,方法public boolean apply(DynamicContext context)用来构造节点内的SQL语句。if (evaluator.evaluateBoolean(test, context.getBindings())该代码便是解析<if test="status !=null and status !=''">test内表达式的关键,如果表达式为true则拼接SQL,否则忽略。
public class IfSqlNode implements SqlNode {
private ExpressionEvaluator evaluator;
private String test;
private SqlNode contents; public IfSqlNode(SqlNode contents, String test) {
this.test = test;
this.contents = contents;
this.evaluator = new ExpressionEvaluator();
} public boolean apply(DynamicContext context) {
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}
}
打开ExpressionEvaluator 类,发现解析表达式使用的是OGNL,如果你使用过古老的Struts框架你应该对它不陌生。通过OgnlCache.getValue(expression, parameterObject);可以看到表达式的值是从缓存中获取的,由此可知MyBatis竟然对表达式也做了缓存,以提高性能。
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;
}
跟进去看看,终于找到了解析表达式的方法private static Object parseExpression(String expression),该方法会先从缓存取值,如果没有便进行解析并放入缓存中,然后调用Ognl.getValue(parseExpression(expression), root)获得表达式的值。
public class OgnlCache {

  private static final Map<String, ognl.Node> expressionCache = new ConcurrentHashMap<String, ognl.Node>();

  public static Object getValue(String expression, Object root) throws OgnlException {
return Ognl.getValue(parseExpression(expression), root);
} private static Object parseExpression(String expression) throws OgnlException {
try {
Node node = expressionCache.get(expression);
if (node == null) {
node = new OgnlParser(new StringReader(expression)).topLevelExpression();
expressionCache.put(expression, node);
}
return node;
} catch (ParseException e) {
throw new ExpressionSyntaxException(expression, e);
} catch (TokenMgrError e) {
throw new ExpressionSyntaxException(expression, e);
}
}
至于Ognl.getValue(parseExpression(expression), root)是如何运作的,如果你有兴趣可以自行跟下去一探究竟,本文就不赘述了。到此为止,我们已经知道MyBatis的表达式是用OGNL处理的了,这一点已经够了。下面我们去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.00也是如此。OGNL对于boolean的定义和JavaScript有点像,即'' == 0 == false。这也就不难理解<if test="status != null and status !=''">and status = #{status}</if>当status=0时出现的问题了,显然0!=''是不成立的,导致表达式的值为false。

将表达式修改为<if test="status != null">and status = #{status}</if>该问题便迎刃而解。该问题的根源还是来自编码的不规范,只有String类型才需要判断是否!='',其他类型完全没有这个必要,可能是开发人员为了省事直接复制上一行拿过来改一改或是所使用的MyBatis生成工具不严谨导致该问题的发生。

这里有必要再提一个“坑”,如果你有类似于String str ="A"; <if test="str!= null and str == 'A'">这样的写法时,你要小心了。因为单引号内如果为单个字符时,OGNL将会识别为Java 中的 char类型,显然String 类型与char类型做==运算会返回false,从而导致表达式不成立。解决方法很简单,修改为<if test='str!= null and str == "A"'>即可。

mybatis 中 if-test 判断大坑的更多相关文章

  1. mybatis中if标签判断字符串相等问题

    mybatis 映射文件中,if标签判断字符串sfyx变量是否是字符串Y的时候,发现并不管用: <if test="sfyx=='Y' "> and 1=1 </ ...

  2. MyBatis中的条件判断单引号双引号的使用

    对于字符串判断, <if test="aIn != 'A'" >会出现问题,系统会试图把'A'转成数字,改为 <if test='aIn != "A&q ...

  3. mybatis 中if标签判断boolean 的写法。

    mybatis 的if 比较标签在比较数值时可以这样写: <if test="value=0"> </if> 在比较字符串时可以这么写: <if te ...

  4. mybatis中多条件判断---choose when的用法

    <select id="getFunctionByPage" resultMap="FunctionRlt"> SELECT K.FUNCTION_ ...

  5. Mybatis if test 中int integer判断非空的坑

    Mybatis 中,alarmType 是int类型.如果alarmType 为0的话,条件判断返回结果为false,其它值的话,返回true. 1 <if test="alarmTy ...

  6. 关于mybatis mapper.xml中的if判断

    场景: 页面上有搜索框进行调节查询,不同搜索框中的内容可以为空. 过程: 点击搜索,前端把参数传给后台,这是后台要把为空的参数过滤掉. 做法: 通常我们在dao层即mapper.xml中进行过滤判断操 ...

  7. mybatis中xml字段空判断及模糊查询

    由于业务特殊的查询需求,需要下面的这种查询,一直感觉模糊不清,本地测试一下顺便做个总结 贴一段xml代码,如下: <if test="receivedName != null and ...

  8. [原创]关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

  9. mybatis中#{}与${}的差别(如何防止sql注入)

    默认情况下,使用#{}语法,MyBatis会产生PreparedStatement语句中,并且安全的设置PreparedStatement参数,这个过程中MyBatis会进行必要的安全检查和转义. # ...

  10. Mybatis中SqlMapper配置的扩展与应用(3)

    隔了两周,首先回顾一下,在Mybatis中的SqlMapper配置文件中引入的几个扩展机制: 1.引入SQL配置函数,简化配置.屏蔽DB底层差异性 2.引入自定义命名空间,允许自定义语句级元素.脚本级 ...

随机推荐

  1. 快速排序基本思想,递归写法,python和java编写快速排序

    1.基本思想 快速排序有很多种编写方法,递归和分递归,分而治之法属于非递归,比递归简单多了.在这不使用代码演示.下面我们来探讨一下快速排序的递归写法思想吧. 设要排序的数组是A[0]……A[N-1], ...

  2. 【Python开发】python PIL读取图像转换为灰度图及另存为其它格式(也可批量改格式)

    例如有一幅图,文件名为"a.jpg'.  读取: from PIL import Image #或直接import Image im = Image.open('a.jpg') 将图片转换成 ...

  3. Design Search Autocomplete System

    Design a search autocomplete system for a search engine. Users may input a sentence (at least one wo ...

  4. # log对数Hash映射优化

    log对数Hash映射优化 利用了一个数学技巧:$\forall k \in [0,35],2^{k} mod 37 互不相等,且恰好取遍整数1-36 $ 应用:将int范围内的\(2^k映射到k\) ...

  5. MySQL数据库增删改查SQL语句(2018整理集合大全)

    查看数据库 show databases;  使用数据库 use 数据库名; 创建数据库 CREATE DATABASE 数据库名; 删除数据库 DROP DATABASE 数据库名; 创建表 cre ...

  6. Fire Net(HDU-1045)(匈牙利最大匹配)(建图方式)

    题意 有一个 n*n 的图,. 代表空白区域,X 代表墙,现在要在空白区域放置结点,要求同一行同一列只能放一个,除非有墙阻隔,问最多能放多少个点 思路 只有在墙的阻隔情况下,才会出现一行/列出现多个点 ...

  7. cf 1163D Mysterious Code (字符串, dp)

    大意: 给定字符串$C$, 只含小写字母和'*', '*'表示可以替换为任意小写字母, 再给定字符串$S,T$, 求$S$在$C$中出现次数-$T$在$C$中出现次数最大值. 设$dp[i][j][k ...

  8. dubbo看这一篇就够了

    为什么要有分布式 近年来微服务.分布式等名词非常的火,那么我们又为什么要进行系统拆分?如何进行拆分呢?阿里的dubbo作为分布式框架的代表,无疑是推动了整个行业技术的进步.以前中小型公司都是一个war ...

  9. 关于memset的几个易错点

    memset(void *s,int ch,size_t n); 作用:将s中当前位置后面的n个字节用 ch 替换并返回 s 注意这里是“字节”而非单位长度,memset不会考虑各个类型的单位长度,只 ...

  10. 【学习总结】Markdown 使用-表格及其居中等格式

    参考: Learning-Markdown (Markdown 入门参考)-表格 Markdown 注:主要是github中的使用 要点: 不管是哪种方式,第一行为表头,第二行为分割表头和主体部分,第 ...