http://cheng-xinwei.iteye.com/blog/2008200

最近在项目使用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"解析出来是一个字符串,两者相等!

【mybatis】IF判断的坑的更多相关文章

  1. Mybatis if判断的坑

    具体情况参考这两篇文章: http://cheng-xinwei.iteye.com/blog/2008200 http://www.cnblogs.com/tv151579/p/3297691.ht ...

  2. 关于MyBatis传入String用于test判断的坑

    不要在心情糟糕的时候写代码,能坑死自己. 今天码代码的时候出现一个问题,脾气暴躁到砸桌子, 在Mybatis传入参数为String并且用 if test 判断的过程中发现 <if test=&q ...

  3. mybatis if判断两个值是否相等存在的坑啊

    1.使用“==”比较 字符类型 的值 用“==”比较的使用场景: 不管你用的什么类型的变量,只要变量的值是字符类型就用“==” 产生原因: 在mybatis中如果<if>标签用一个“=”判 ...

  4. Mybatis的失误填坑-java.lang.Integer cannot be cast to java.lang.String

    Mybatis的CRUD小Demo 为方便查看每次的增删改结果,封装了查询,用来显示数据库的记录: public static void showInfo(){ SqlSession session ...

  5. Mybatis if 判断等于一个字符串

    在做开发的时候遇到这样一个问题:当传入的type的值为y的时候,if判断内的sql也不会执行.   <if test="type=='y'">   and status ...

  6. mybatis匹配字符串的坑

    where语句中我们经常会做一些字符串的判断,当传入的字符串参数为纯数字时,在mybatis的条件语句test里匹配全数字字符串需要注意会有如下现象: 所以里面的字符串需要加单引号,mybatis是匹 ...

  7. MyBatis 一级缓存避坑

    MyBatis 一级缓存(MyBaits 称其为 Local Cache)无法关闭,但是有两种级别可选: package org.apache.ibatis.session; /** * @autho ...

  8. MyBatis正在爬的坑

    换了份工作,开始接触Mybatis,开一篇文章记录一下自己遇到的坑 2018-06-20 今天遇到了一个问题,编好的sql语句在数据库可以执行但是写到程序里边就GG,什么问题呢?一直纠结在程序哪里写错 ...

  9. LocalDatetime 与 mybatis、json的坑

    总所周知,localdatetime是jdk8 推出的关于日期计算非常方便地一个类,一旦开始用上就欲罢不能.但是在使用的时候,坑还是蛮多的. 一.mybatis与LocalDatetime 如果直接将 ...

随机推荐

  1. PLSQL Developer 中文乱码踩坑记

    环境 操作系统版本: Windows 7 PL/SQL 版本: 12.0.1.1814 原因 由于 Oracle 服务器端和客户端字符集编码不一致引起的. 注意点 写在最前面,减少踩坑!!! 网上教程 ...

  2. GeoGebra函数使用

    分段函数使用 输入指令: If(x < -2, x, -2 < x < 2, x², x > 2, x)

  3. 谁说 Vim 不好用?送你一个五彩斑斓的编辑器!

    相信大家在使用各种各样强大的 IDE 写代码时都会注意到,代码中各种类型的关键字会用独特的颜色标记出来,然后形成一套语法高亮规则.这样不仅美观,而且方便代码的阅读. 而在上古神器 Vim 中,我们通常 ...

  4. 再接再厉,JSONViewer现已支持Firefox、Microsoft Edge、360浏览器,可能是最好用的JSON格式化工具

    之前写的JSONViewer,截至目前在谷歌商店里已经有1000+的自然下载量了 为什么开发JSONViewer? 日常开发中,拿到接口输出的JSON一般会去在线的JSON格式化网站查看,但是在线格式 ...

  5. 数据结构(C语言版)---二叉树

    1.二叉树:任意一个结点的子结点个数最多两个,且子结点的位置不可更改,二叉树的子树有左右之分. 1)分类:(1)一般二叉树(2)满二叉树:在不增加树的层数的前提下,无法再多添加一个结点的二叉树就是满二 ...

  6. Linux-Deepin 下开启SSH远程登陆

    #### 关于deepin系统安装ssh后,root超级用户登录报错的完美解决方案! 最近刚刚接触到deepin,觉得,wow,除了mac,还有这么好看的非win系统,而且第测出那个Linux,宽容度 ...

  7. 牛顿迭代法的理解与应用( x 的平方根)

    题目来源与LeetCode算法题中的第69题,具体内容如下(点击查看原题): 实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只 ...

  8. DEV gridview 合并单元格

    private void gv_docargo_CellMerge(object sender, DevExpress.XtraGrid.Views.Grid.CellMergeEventArgs e ...

  9. 分享一个404页面(猴子动态SVG图)

    说明 在CSDN看到的一个404界面,简洁大气非常棒我的个人网站已经用上了! 代码 防止原页面失效,代码 粘贴在下面 <!DOCTYPE html> <html lang=" ...

  10. JasperReports入门教程(二):中文打印

    JasperReports入门教程(二):中文打印 背景 在上一篇中我们介绍了JasperReport的基本入门,也展示了一个报表.但是我们的示例都是使用的英文,如果我们把需要打印的数据改为中文会怎么 ...