最近在项目使用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. Play初识

    2015年11月21日,写下这篇<Play初识> Play是神马呢?不是Google Play,而是一个java的web框架,因为它抛弃了传统的servlet模式的做法,国内网络连接pla ...

  2. 分布式开放消息系统(RocketMQ)的原理与实践(转)

    转自:http://www.jianshu.com/p/453c6e7ff81c 分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回 ...

  3. 生产环境中使用Docker Swarm的一些建议

    译者按: 实践中会发现,生产环境中使用单个Docker节点是远远不够的,搭建Docker集群势在必行.然而,面对Kubernetes, Mesos以及Swarm等众多容器集群系统,我们该如何选择呢?它 ...

  4. 【网站管理1】_dede织梦后台如何发布文章

    对于新手可能不了解,dede织梦后台是如何发文章的.下面说下我的经验. 发布文章步骤 1.打开浏览器,推介谷歌,360极速浏览器,火狐浏览器,输入网站后台网址   出现如下图登入界面 2.输入账号密码 ...

  5. Github SSH 的key 生成步骤下

    Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置. github的SSH配置如下: 一 . 设置Git的user name和email: $ git ...

  6. Natas Wargame Level 15 Writeup(Content-based Blind SQL Injection)

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAq4AAACGCAYAAAAcnwh0AAAABHNCSVQICAgIfAhkiAAAIABJREFUeF

  7. 弹性盒布局display:flex详解

    一:弹性盒子 随着响应式设计的流行,网站开发者在设计网页布局时往往要考虑到页面在适配不同分辨率的浏览器时其内部组件位置大小都会产生变化,因此需要设计者根据窗口尺寸来调整布局,从而改变组件的尺寸和位置, ...

  8. 初码-Azure系列-如何在控制面板中选择中文版操作系统

    之前在文章<初码-Azure系列-记一次从阿里云到Azure的迁移和部署>中说到,默认的Windows Server 2016操作系统是英文版,后来摸索出中文版的方法,如下:

  9. 今天重装系统后,Wdows更新提示“windows update当前无法检查更新,因为未运行服务。您可能需要重新启动计算机”

    到百度搜了常用的解决方法,就是用命令提示符,但对我的情况不管用,提示“拒绝访问”.后来在08绿软站的一篇文章中找到了解决办法.原文如下(我本人也是用的第四种方法解决的): 试了下面几种解决方法,第四种 ...

  10. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...