`
cheng.xinwei
  • 浏览: 79662 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

【mybatis】IF判断的坑

阅读更多
最近在项目使用mybatis中碰到个问题
<if test="type=='y'">
    and status = 0 
</if>


当传入的type的值为y的时候,if判断内的sql也不会执行,抱着这个疑问就去看了mybatis是怎么解析sql的。下面我们一起来看一下mybatis 的执行过程。

DefaultSqlSession.class  121行
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }




在 executor.query(ms, wrapCollection(parameter), rowBounds, handler);
执行到BaseExecutor.class执行器中的query方法
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

在query的方法中看到boundSql,是通过 ms.getBoundSql(parameter);获取的。

再点进去可以看到MappedStatement.class类中的getBoundSql方法
public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.size() <= 0) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }


看到其中有sqlSource.getBoundSql(parameterObject); sqlsource是一个接口。
/**
 * 
 * This bean represets the content of a mapped statement read from an XML file
 * or an annotation. It creates the SQL that will be passed to the database out
 * of the input parameter received from the user.
 * 
 */
public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}

类中getBoundSql是一个核心方法,mybatis 也是通过这个方法来为我们构建sql。BoundSql 对象其中保存了经过参数解析,以及判断解析完成sql语句。比如<if> <choose> <when> 都回在这一层完成,具体的完成方法往下看,那最常用sqlSource的实现类是DynamicSqlSource.class



public class DynamicSqlSource implements SqlSource {

  private Configuration configuration;
  private SqlNode rootSqlNode;

  public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
  }

  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    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);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

}

核心方法是调用了rootSqlNode.apply(context); rootSqlNode是一个接口
public interface SqlNode {
  boolean apply(DynamicContext context);
}

可以看到类中 rootSqlNode.apply(context); 的方法执行就是一个递归的调用,通过不同的
实现类执行不同的标签,每一次appll是完成了我们<></>一次标签中的sql创建,计算出标签中的那一段sql,mybatis通过不停的递归调用,来为我们完成了整个sql的拼接。那我们主要来看IF的实现类IfSqlNode.class
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;
  }

}


可以看到IF的实现中,执行了 if (evaluator.evaluateBoolean(test, context.getBindings()))  如果返回是false的话直接返回,否则继续递归解析IF标签以下的标签,并且返回true。那继续来看 evaluator.evaluateBoolean 的方法

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;
  }


关键点就在于这里,在OgnlCache.getValue中调用了Ognl.getValue,看到这里恍然大悟,mybatis是使用的OGNL表达式来进行解析的,在OGNL的表达式中,'y'会被解析成字符,因为java是强类型的,char 和 一个string 会导致不等。所以if标签中的sql不会被解析。具体的请参照 OGNL 表达式的语法。到这里,上面的问题终于解决了,只需要把代码修改成:
<if test='type=="y"'>
    and status = 0 
</if>

就可以执行了,这样"y"解析出来是一个字符串,两者相等!
分享到:
评论
10 楼 zxmsdyz 2017-03-07  
解决了我三年后的问题   看了下源码凌乱了,楼主分析的不错有耐心啊
9 楼 不忘初心ok 2016-10-26  
厉害,解决了
8 楼 浑身卟域帖 2016-10-21  
7 楼 u011654693 2016-05-26  
问题解决了,非常感谢。
6 楼 wangdianyong 2016-01-27  
看源码解决问题  真实吊
5 楼 zhengalways 2015-10-21  
厉害,看源码是硬道理
4 楼 幻樱释 2015-05-06  
不错的解析 过程
3 楼 llhua2329 2014-09-30  
2 楼 yb_butterfly 2014-03-04  
今天也遇到这个问题了,看了楼主解析,恍然大悟啊
1 楼 krdsg 2014-01-23  
学到了,困扰我20多年的问题终于解决了

相关推荐

    Mybatis3 if判断字符串变态写法

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

    mybatis中&lt;if&gt;标签bool值类型为false判断方法

    `&lt;if&gt;`标签是动态SQL的一种常用方式,主要用于条件判断。当我们需要根据变量的值来决定某段SQL代码是否执行时,`&lt;if&gt;`标签就派上了用场。 在问题描述中,开发者遇到的问题是在处理`&lt;if&gt;`标签时,bool类型的变量`by...

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

    其中,`if`标签是MyBatis动态SQL的重要组成部分,它允许我们在构建SQL语句时根据条件进行动态判断。本文将详细讨论在MyBatis映射文件中,`if`标签判断字符串相等的两种方法。 ### 方法一:使用OGNL表达式 MyBatis...

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

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

    mybatis 动态sql及参数传递

    目录 使用场景 动态标签 if标签 where标签 choose、when、otherwise 标签 ...if标签通常用那个胡where语句,update语句,insert语句中,通过判断参数值来决定是否使用某个查询条件,判断是否更新某一个字段或插入某个字段

    mybatis demo mybatis 实例

    MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解进行配置和原始映射,将接口和Java的POJOs...

    mybatis-3-mybatis-3.2.6

    12. **动态SQL**:MyBatis提供了强大的动态SQL功能,允许在XML映射文件中直接编写条件语句,如if、choose、when、otherwise等。 13. **缓存**:MyBatis支持本地缓存和二级缓存,可以提高数据读取速度,减少数据库...

    MyBatis 官方笔记 + MyBatis 3.5.10 官方英文文档

    - **动态 SQL**:通过 `&lt;if&gt;`、`&lt;choose&gt;`、`&lt;when&gt;`、`&lt;otherwise&gt;`、`&lt;foreach&gt;` 等标签实现 SQL 语句的动态生成。 - **结果映射(ResultMap)**:用于复杂对象的映射,处理一对一、一对多、多对多等关系。 4. ...

    MybatisX-1.5.7

    4. **数据权限控制**:MybatisX支持基于角色的数据权限控制,可以在Mapper层实现对数据的过滤,避免在Service层进行繁琐的权限判断。 5. **事务管理**:提供统一的事务处理机制,支持全局事务和局部事务,简化事务...

    mybatisx.zip

    4. **动态SQL助手**:支持自动生成动态SQL的if、choose、when、otherwise等标签,使动态SQL的编写变得简单直观。 5. **条件查询构建**:插件能帮助构建复杂的条件查询,通过简单的拖拽操作即可生成对应的XML片段,...

    mybatis入门案例 mybatis入门案例

    mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例mybatis入门案例...

    mybatis的中文api文档

    MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以对配置和原生Map使用简单的XML或注解,将接口和Java的POJOs...

    mybatis3.5.7.zip

    MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 ...

    MyBatis3 API 中文文档

    - if、choose、when、otherwise:根据不同的条件判断,动态地加入SQL片段。 - trim、where、set:动态地处理SQL语句的前后缀。 - foreach:用于处理遍历集合或数组,并动态地加入SQL片段。 8. Java API MyBatis还...

    mybatis,mybatis+mysql

    3. **动态SQL**:MyBatis的一个强大特性是它的动态SQL功能,允许你在XML映射文件中编写条件判断、循环等复杂的SQL逻辑,使得代码更加灵活和可维护。 4. **调用存储过程**:MyBatis也支持调用MySQL的存储过程。在XML...

    MyBatis3官方中文文档

    MyBatis是一款流行的Java持久层框架,它的设计理念是通过简单的XML或注解用于配置和映射原始类型、接口和Java POJOs(Plain Old Java Objects,普通老式Java对象)为数据库中的记录。MyBatis可以与各种数据库交互,...

    MyBatisDemo

    例如,使用`&lt;if&gt;`、`&lt;choose&gt;`、`&lt;when&gt;`、`&lt;otherwise&gt;`等标签进行条件判断,使得一个映射文件可以处理多种不同的查询场景。 事务管理是MyBatis中的另一个关键点。在MyBatis中,你可以手动管理事务,或者配置`...

    MyBatis入门.pdf

    MyBatis入门 MyBatis是一个优秀的持久层框架,使用XML将SQL与程序解耦,便于维护。MyBatis学习简单,执行高效,是JDBC的延伸。MyBatis提供了数据库增删改查的便捷操作,极大提高了开发效率、统一的编码规则、利于...

    mybatis中文离线文档

    MyBatis是一个优秀的Java持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解进行配置和原始映射,将接口和Java的...

    MyBatis 中文官方文档

    MyBatis 提供了强大的映射元素,如`&lt;resultMap&gt;`用于定义结果集映射,`&lt;association&gt;`和`&lt;collection&gt;`用于处理复杂对象关系,以及`&lt;choose&gt;`, `&lt;when&gt;`, `&lt;otherwise&gt;`等结构,实现类似Java的if-else逻辑。...

Global site tag (gtag.js) - Google Analytics