`

MYBATIS中if test中注意的事项

 
阅读更多
http://www.jianshu.com/p/91ed365c0fdd

现有一项目,ORM框架使用MyBatis,在进行列表查询时,选择一状态(值为0)通过动态SQL拼接where条件但无法返回正常的查询结果,随后进行排查。
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 SQLand status = 0并未正常拼接,也就是说test内的表达式为false,从而导致查询结果错误。但是,显然该值(Integer :0)!= null也!= ' ',应该为true才对。

通过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中&lt;if&gt;标签bool值类型为false判断方法

    主要给大家介绍了关于mybatis中&lt;if&gt;标签bool值类型为false判断方法,文中通过示例代码介绍的非常详细,对大家学习或者使用mybatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

    Mybatis3 if判断字符串变态写法

    主要介绍了Mybatis3 if判断字符串变态的写法,非常不错,具有参考借鉴价值,需要的朋友参考下

    MyBatis动态Sql之if标签的用法详解

    主要介绍了MyBatis动态Sql之if标签的用法,本文给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下

    springmybatis

    sessionFactory 用的,里面主要包含了数据库连接相关东西,还有 java 类所对应的别名,比如 &lt;typeAlias alias="User" type="com.yihaomen.mybatis.model.User"/&gt; 这个别名非常重要,你在 具体的类的映射中,比如User...

    mybatis动态sqlmybatis动态sqlmybatis动态sql

    1. if标签:if标签是动态SQL中最常用的功能之一。它允许我们根据条件判断是否拼接SQL语句的一部分。使用if标签时,可以使用Java代码对条件进行判断,以决定是否添加对应的SQL片段。 例如: ```xml SELECT * FROM ...

    mybatis动态sql.doc

    在 MyBatis 中,可以使用 `&lt;if&gt;`、`&lt;choose&gt;`、`&lt;when&gt;`、`&lt;otherwise&gt;` 等标签来实现动态 SQL。以下是一个简单的示例: ```xml SELECT * FROM user &lt;if test="username != null and username != ''"&gt; AND ...

    MyBatis使用TypeHandler示例

    此项目为MyBatis使用TypeHandler的示例 注意,测试需要在MySql中建表 附:建表语句及数据,导入Mysql运行即可 在Mysql命令窗口输入 set names 'utf8'; source 1.sql 将以下文件保存为1.sql DROP TABLE IF EXISTS `...

    springboot集成spring mvc,mybatis

    springboot集成spring mvc,mybatis的一个工程demo。maven工程。...INSERT INTO `sys_user` VALUES ('1015', 'test1', '123456', 'test1@mybatis.tk', 'test info', 0x010203, '2018-05-15 10:30:39');

    mybatis动态sql

    MyBatis 提供了强大的动态... &lt;if test="name != null"&gt; AND name = #{name} &lt;/if&gt; &lt;if test="age != null"&gt; AND age = #{age} &lt;/if&gt; , , &lt;otherwise&gt;:类似于 Java 中的 switch-case-default 结构。 xml ...

    如何使用CASE WHEN语法判断入参代替if test=user-name != null and user-name !=

    传统的Mybatis语法`&lt;if test="user_name != null and user_name != ''"&gt;`条件判断虽常见,却也暴露了其平台局限性和适用场景的不足。本资源旨在深入探讨如何运用CASE WHEN语法巧妙绕过这些限制,实现更广泛平台兼容...

    mybatis trim标签的使用

    &lt;if test="id != null"&gt; id, &lt;/if&gt; &lt;if test="name != null"&gt; name, &lt;/if&gt; &lt;if test="seq != null"&gt; seq, &lt;/if&gt; &lt;if test="description != null"&gt; description, &lt;/if&gt; &lt;if test="status !=...

    MyBatis SonarQube Plugin自定义规则用于检查 MyBatis Mapper 文件中的风险 SQL.zip

    mybatis动态sql 1.什么是动态SQL? Mabits是一个Java持久化框架,它提供了动态SQL的功能。...&lt;if&gt;标签通常包含一个test属性,该属性被用于指定条件表达式。如果表达式的结果为true,则&lt;if&gt;标签内的内容会被包含在生成的

    MyBatis-Flex 一个优雅的 MyBatis 增强框架.zip

    mybatis动态sql 1.什么是动态SQL? Mabits是一个Java持久化框架,它提供了动态SQL的功能。...&lt;if&gt;标签通常包含一个test属性,该属性被用于指定条件表达式。如果表达式的结果为true,则&lt;if&gt;标签内的内容会被包含在生成的

    Mybatis特殊字符处理的详解

    Mybatis特殊字符处理,Mybatis中xml文件特殊字符的处理,这里提供了解决办法及实例,大家可以参考下: 一、问题描述: 查询时,需要获取时间区间内的数据,如下: &lt;if test=startTime&gt; and l.CREATE_TIME &gt;= #{...

    springboot集成mybatis动态sql.zip

    mybatis动态sql 1.什么是动态SQL? Mabits是一个Java持久化框架,它提供了动态SQL的功能。...&lt;if&gt;标签通常包含一个test属性,该属性被用于指定条件表达式。如果表达式的结果为true,则&lt;if&gt;标签内的内容会被包含在生成的

    Mybatis动态SQL高级映射.zip

    mybatis动态sql 1.什么是动态SQL? Mabits是一个Java持久化框架,它提供了动态SQL的功能。...&lt;if&gt;标签通常包含一个test属性,该属性被用于指定条件表达式。如果表达式的结果为true,则&lt;if&gt;标签内的内容会被包含在生成的

    mybatis语法增强框架, 综合了mybatis plus, danymic sql, jpa等框架特性和优点生成代码.zip

    mybatis动态sql 1.什么是动态SQL? Mabits是一个Java持久化框架,它提供了动态SQL的功能。...&lt;if&gt;标签通常包含一个test属性,该属性被用于指定条件表达式。如果表达式的结果为true,则&lt;if&gt;标签内的内容会被包含在生成的

    MyBatis Dynamic SQL 动态sql案例.zip

    mybatis动态sql 1.什么是动态SQL? Mabits是一个Java持久化框架,它提供了动态SQL的功能。...&lt;if&gt;标签通常包含一个test属性,该属性被用于指定条件表达式。如果表达式的结果为true,则&lt;if&gt;标签内的内容会被包含在生成的

    MyBatisCodeHelper-Pro.zip

    Generate mybatis sql based on mybatis interface method name like spring data jpa, with this, you don't have to write most sql for non join query support generate statement with if test Database ...

    mybatis 增强工具包,简化 CRUD 操作.zip

    mybatis动态sql 1.什么是动态SQL? Mabits是一个Java持久化框架,它提供了动态SQL的功能。...&lt;if&gt;标签通常包含一个test属性,该属性被用于指定条件表达式。如果表达式的结果为true,则&lt;if&gt;标签内的内容会被包含在生成的

Global site tag (gtag.js) - Google Analytics