最近在项目使用mybatis中碰到个问题

  1. <if test="type=='y'">
  2. and status = 0
  3. </if>

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

DefaultSqlSession.class  121行

  1. public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  2. try {
  3. MappedStatement ms = configuration.getMappedStatement(statement);
  4. executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  5. } catch (Exception e) {
  6. throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  7. } finally {
  8. ErrorContext.instance().reset();
  9. }
  10. }

在 executor.query(ms, wrapCollection(parameter), rowBounds, handler); 
执行到BaseExecutor.class执行器中的query方法

  1. public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  2. BoundSql boundSql = ms.getBoundSql(parameter);
  3. CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
  4. return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  5. }

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

再点进去可以看到MappedStatement.class类中的getBoundSql方法

  1. public BoundSql getBoundSql(Object parameterObject) {
  2. BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  3. List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  4. if (parameterMappings == null || parameterMappings.size() <= 0) {
  5. boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
  6. }
  7. // check for nested result maps in parameter mappings (issue #30)
  8. for (ParameterMapping pm : boundSql.getParameterMappings()) {
  9. String rmId = pm.getResultMapId();
  10. if (rmId != null) {
  11. ResultMap rm = configuration.getResultMap(rmId);
  12. if (rm != null) {
  13. hasNestedResultMaps |= rm.hasNestedResultMaps();
  14. }
  15. }
  16. }
  17. return boundSql;
  18. }

看到其中有sqlSource.getBoundSql(parameterObject); sqlsource是一个接口。

  1. /**
  2. *
  3. * This bean represets the content of a mapped statement read from an XML file
  4. * or an annotation. It creates the SQL that will be passed to the database out
  5. * of the input parameter received from the user.
  6. *
  7. */
  8. public interface SqlSource {
  9. BoundSql getBoundSql(Object parameterObject);
  10. }

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

  1. public class DynamicSqlSource implements SqlSource {
  2. private Configuration configuration;
  3. private SqlNode rootSqlNode;
  4. public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
  5. this.configuration = configuration;
  6. this.rootSqlNode = rootSqlNode;
  7. }
  8. public BoundSql getBoundSql(Object parameterObject) {
  9. DynamicContext context = new DynamicContext(configuration, parameterObject);
  10. rootSqlNode.apply(context);
  11. SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
  12. Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
  13. SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
  14. BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  15. for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
  16. boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
  17. }
  18. return boundSql;
  19. }
  20. }

核心方法是调用了rootSqlNode.apply(context); rootSqlNode是一个接口

  1. public interface SqlNode {
  2. boolean apply(DynamicContext context);
  3. }

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

  1. public class IfSqlNode implements SqlNode {
  2. private ExpressionEvaluator evaluator;
  3. private String test;
  4. private SqlNode contents;
  5. public IfSqlNode(SqlNode contents, String test) {
  6. this.test = test;
  7. this.contents = contents;
  8. this.evaluator = new ExpressionEvaluator();
  9. }
  10. public boolean apply(DynamicContext context) {
  11. if (evaluator.evaluateBoolean(test, context.getBindings())) {
  12. contents.apply(context);
  13. return true;
  14. }
  15. return false;
  16. }
  17. }

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

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

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

  1. <if test='type=="y"'>
  2. and status = 0
  3. </if>

就可以执行了,这样"y"解析出来是一个字符串,两者相等!

mybatis if test加筛选条件的更多相关文章

  1. jqgrid 表格中筛选条件的多选下拉,树形下拉 ;文本框清除插件;高级查询多条件动态筛选插件[自主开发]

    /** * @@desc 文本框清除按钮,如果isAutoWrap为false当前文本框父级必须是relative定位,boostrap参考input-group * @@author Bear.Ti ...

  2. Mybatis中动态SQL多条件查询

    Mybatis中动态SQL多条件查询 mybatis中用于实现动态SQL的元素有: if:用if实现条件的选择,用于定义where的字句的条件. choose(when otherwise)相当于Ja ...

  3. MySql 筛选条件、聚合分组、连接查询

    筛选条件 比较运算符 等于: = ( 注意!不是 == ) 不等于: != 或 <> 大于: > 大于等于: >= 小于: < 小于等于: <= IS NULL I ...

  4. element ui table表头动态筛选条件

    本文主要实现:根据el-table表格数据自动生成表头筛选条件的方法,可根据表格数据动态调整. el-table表格的表头增加筛选功能,大家平时都是怎么实现的呢?先看看官方文档的例子: 1 <t ...

  5. sql之表连接 筛选条件放在 连接外和放在连接里的区别

    使用一个简单的例子,说明他们之间的区别 使用的表:[Sales.Orders]订单表和[Sales.Customers]客户表,和上一篇博客的表相同 业务要求:查询出 : 所有的用户 在 2012-1 ...

  6. 关于 Mybatis 设置懒加载无效的问题

    看了 mybatis 的教程,讲到关于mybatis 的懒加载的设置: 只需要在 mybatis 的配置文件中设置两个属性就可以了: <settings> <!-- 打开延迟加载的开 ...

  7. MySQL 误删数据、误更新数据(update,delete忘加where条件)

    MySQL 误操作后数据恢复(update,delete忘加where条件) 关键词:mysql误删数据,mysql误更新数据 转自:https://www.cnblogs.com/gomysql/p ...

  8. vue的data的数据进行指定赋值,用于筛选条件的清空,或者管理系统添加成功后给部分数据赋值为空

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. MySQL 误操作后数据恢复(update,delete忘加where条件)

    在数据库日常维护中,开发人员是最让人头痛的,很多时候都会由于SQL语句写的有问题导致服务器出问题,导致资源耗尽.最危险的操作就是在做DML操作的时候忘加where条件,导致全表更新,这是作为运维或者D ...

随机推荐

  1. JAVA的HashTable源码分析

    Hashtable简介 Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长.Hashtable ...

  2. Java7中的ForkJoin并发框架初探(上)——需求背景和设计原理

    原文:发表于 2013 年 8 月 26 日 由 三石 0. 处理器发展和需求背景 回想一下并发开发的初衷,其实可以说是有两点,或者说可以从两个方面看. 对于单核的处理器来说,在进行IO操作等比较费时 ...

  3. ZooKeeper实践:(1)配置管理

    一. 前言     配置是每个程序不可或缺的一部分,配置有多重方式:xml.ini.property.database等等,从最初的单机环境到现在的分布式环境. 1. 以文件的格式存储配置,修改任何都 ...

  4. java中多种写文件方式的效率对比实验

    一.实验背景 最近在考虑一个问题:“如果快速地向文件中写入数据”,java提供了多种文件写入的方式,效率上各有异同,基本上可以分为如下三大类:字节流输出.字符流输出.内存文件映射输出.前两种又可以分为 ...

  5. keydown - > keypress - > keyup 用法和区别

    英文输入法:   事件触发顺序:keydown - > keypress - > keyup   中文输入法:   firfox:输入触发keydown,回车确认输入触发keyup chr ...

  6. JAVA栈帧

    简介 Java栈是一块线程私有的内存空间.java堆和程序数据相关,java栈就是和线程执行密切相关的,线程的执行的基本行为是函数调用,每次函数调用的数据都是通过java栈来传递的. Java栈与数据 ...

  7. NodeJS 入门第二天(EJS模板)

    一.复习 复习:Node.js开发服务器,数据.路由.本地关心的效果,交互: Node.js实际上是极客开发出的一个小玩具,不是银弹.有着别人不具备的怪异特点: 单线程.Non-blocking I/ ...

  8. 我和 flow.ci 的第一次亲密接触

    编者按:本文转载自 flow.ci 用户 @君赏 的实践分享,原文链接这里. 这不是第一次听说 flow.ci ,记得当时 fir.im 新出这个服务的时候,我也是心情十分激动的去尝试,结果是只支持安 ...

  9. 关于querySelectorAll的一个坑

    刚学JS的DOM操作时,就知道了匹配一堆元素,会获得NodeList和HTMLCollection这两个对象,不过当时并没有深入去研究两者的区别 因为无论是NodeList还是HTMLCollecti ...

  10. sql备份(.bak文件备份)

    第一步: 右键需要备份的数据库(这里以 MyDB 为例)-->任务-->备份