`

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值时需要注意以下几点: 1. 当需要基于bool值进行判断时,直接使用变量名作为测试条件,如`test="byId"`,而不要使用`test="byId != null"`,因为bool值不会是null。 2. ...

    Mybatis3 if判断字符串变态写法

    在Mybatis3中,`if`标签用于进行条件判断,以动态地控制SQL语句的生成。在处理字符串时,我们需要特别注意字符串的比较方式,因为不正确的写法可能导致异常。这里我们将深入探讨如何正确使用`if`标签来判断字符串。 ...

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

    MyBatis动态Sql之if标签是MyBatis框架中的一种动态Sql语言,用于在查询语句中根据条件生成不同的Sql语句。if标签是MyBatis动态Sql语言中的一种常用标签,用于根据条件执行不同的Sql语句。 if标签的基本用法 if标签...

    mybatis_test09_mybatis_Mine!_

    MyBatis还支持动态SQL,这使得在XML映射文件中可以编写条件语句,比如IF、WHERE、choose(when/otherwise)、trim(where/sets/foreach)等元素,极大地提高了SQL的灵活性。 此外,MyBatis的注解方式也是常用的一种...

    Mybatis中配置Mapper的方法

    在传统的MyBatis配置中,我们通常会在`mybatis-config.xml`文件中声明SqlSessionFactory,并在资源目录下的`mappers`子目录创建XML映射文件。例如,一个名为`UserMapper.xml`的文件,内容可能包含如下结构: ```...

    Mybatis_test.zip

    在Mybatis_test中,我们可能会看到一个名为`mybatis-config.xml`的主配置文件,其中包含了数据源配置、事务管理器以及映射文件的位置等信息。 描述中的"mybatis数据库查询"暗示了这个压缩包可能包含有关如何执行...

    MyBatis中如何优雅的使用枚举详解

    5. 配置自定义类型处理器配置自定义类型处理器在MyBatis的配置文件中,添加我们自定义的枚举类型处理器: ```xml ``` 6. 使用枚举在Mapper接口及XML映射文件中,现在可以放心地使用我们的枚举类型了。例如,在...

    mybatis之动态SQL

    在实际项目中,我们还需要注意 SQL 注入的问题。MyBatis 提供了预编译的 SQL 语句和参数绑定机制,能有效防止 SQL 注入攻击。同时,合理使用 MyBatis 的缓存功能,还可以进一步提升应用性能。 总之,MyBatis 的动态...

    mybatis 映射文件中if标签判断字符串相等的两种方式

    本文将详细讨论在MyBatis映射文件中,`if`标签判断字符串相等的两种方法。 ### 方法一:使用OGNL表达式 MyBatis的`if`标签内支持OGNL(Object-Graph Navigation Language)表达式,这是一种强大的表达式语言,用于...

    mybatis中的动态sql, 涉及 where trim set if foreach等

    本文将详细介绍MyBatis中涉及`where`, `trim`, `set`, `if`, `foreach`等关键字的动态SQL处理方式,并通过具体的示例帮助理解。 #### 二、动态SQL概述 动态SQL是指在运行时根据条件动态构建SQL语句的技术。在...

    mybatis_test

    7. **动态SQL**:MyBatis的动态SQL功能强大,通过`&lt;if&gt;`、`&lt;choose&gt;`、`&lt;when&gt;`、`&lt;otherwise&gt;`、`&lt;where&gt;`、`&lt;foreach&gt;`等标签,可以在XML映射文件中编写条件语句,实现灵活的SQL构建。 8. **缓存机制**:MyBatis...

    mybatis、mybatis详细设计、mybatis配置

    本篇文章将深入探讨MyBatis的设计原理、详细配置以及在实际项目中的应用。 首先,MyBatis的核心设计理念是将SQL语句与Java代码解耦。它通过XML或注解的方式将SQL语句配置到Mapper接口或Mapper XML文件中,然后通过...

    MyBatis 3 用户指南中文英文

    &lt;if test="name != null"&gt; AND name like #{name} &lt;/if&gt; ``` 七、缓存机制 MyBatis提供了本地缓存和二级缓存,可以有效减少对数据库的访问,提高系统性能。 八、事务管理 MyBatis支持编程式事务管理和声明式事务...

    springmybatis

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

    mybatis动态sql

    MyBatis 中用于实现动态 SQL 的元素主要有 if、choose、where 等元素。 1. if 元素 if 元素是 MyBatis 中最简单的条件判断元素。利用 if 语句我们可以实现某些简单的条件选择。例如: ```xml select * from t_...

    mybatis Mapper.xml中传参多选 字符串形式逗号分隔 AND中拼接OR.rar

    在Mybatis中,我们经常需要处理复杂的SQL查询,其中涉及到多条件筛选,这些条件可能是可选的,并且可能需要在`AND`与`OR`之间灵活切换。标题和描述所提及的问题是关于如何在Mapper.xml文件中处理字符串形式的参数,...

    mybatis自定义标签.zip

    MyBatis是一个强大的Java持久层框架,它允许开发者将SQL语句直接集成到XML映射文件或Java注解中,提供了灵活的数据库交互方式。在实际开发中,有时我们需要根据项目需求实现特定的功能,比如动态生成SQL或者进行复杂...

    MyBatis-03动态SQL-01.<if>元素的使用

    在这个主题中,我们将深入探讨`&lt;if&gt;`元素在MyBatis动态SQL中的应用。 `&lt;if&gt;`元素是MyBatis动态SQL中最基础的条件判断标签,用于在构建SQL语句时进行条件判断。它的基本语法结构如下: ```xml &lt;if test="property...

    mybatis中使用ognl共4页.pdf.zip

    **四、注意事项** 1. **安全问题**:由于OGNL的强大,如果不小心可能会导致SQL注入等安全问题。因此,在使用OGNL动态构建SQL时,应确保输入参数已经过验证和过滤。 2. **性能影响**:虽然OGNL提供了灵活性,但其...

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

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

Global site tag (gtag.js) - Google Analytics