最近在项目使用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. python基本运算

    环境:python3.x a,b = 60,164 一.算数运算符 操作符 描述 例子 + 加法 a+b = 224 - 减法 a-b = -104 * 乘法 a*b = 9840 / 除(保留小数位 ...

  2. Firefox实用插件记录

    之前总结过一个软件推荐的小文,用来记录一直以来在软件开发过程中遇到的各种实用的软件.后来发现里面越来越多的记录了Firefox的插件,所以今天决定单独抽出一个页面来记录Firefox的插件.因为平时大 ...

  3. Python的RSA加密和PBE加密

    最近在写接口的时候,遇到了需要使用RSA加密和PBE加密的情况,对方公司提供的DEMO都是JAVA的,我需要用python来实现. 在网上搜了一下,python的RSA加密这块写的还是比较多的,但是P ...

  4. CSS预编译器:Sass(入门),更快的前端开发

    SASs是由美国注册会计师协会(AICPA)下属审计准则委员会(ASB)发布,是为了便于注册会计师执行和落实一般公认审计准则(GAAS). Sass 扩展了 CSS3,增加了规则.变量.混入.选择器. ...

  5. Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...

  6. 《安卓网络编程》之第六篇 Android中的WIFI和蓝牙

    关于WIFI就不多介绍啦,直接来个段子吧. 问:“WiFi对人体有伤害么?” 答:“不清楚,反正没有WiFi我就浑身不舒服. 比较重要的一点就是WifiManager  wm=(WifiManager ...

  7. 用css3过滤做遮罩效果

    <!DOCTYPE html><html ng-app="myApp" ng-controller="myController">< ...

  8. VR大时代-全景智慧城市搭建是一个任重而道远的任务

    全景智慧城市搭建是一个任重而道远的任务,但是也促进了实体市场的蓬勃发展与进步.VR技术改变了人们以往的娱乐方式,而全景智慧城市将会彻底改变人们的生活习惯.VR是未来的计算平台,更是人力发展历史中,技术 ...

  9. 特朗普退出《巴黎协定》:python词云图舆情分析

    1 前言 2017年6月1日,美国特朗普总统正式宣布美国退出<巴黎协定>.宣布退出<巴黎协定>后,特朗普似乎成了“全球公敌”. 特斯拉总裁马斯克宣布退出总统顾问团队 迪士尼董事 ...

  10. Python教程(2.6)——list和tuple简介

    Python中内置的类型有list和tuple. List list类似于C/C++的数组,可以存储多个数字.例如你可能会需要存储一个班里所有人的名字.这时就可以用到list.list中存储的数据叫做 ...