zoukankan      html  css  js  c++  java
  • mybatis if test加筛选条件

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

    Xml代码  收藏代码
    1. <if test="type=='y'">  
    2.     and status = 0   
    3. </if>  



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

    DefaultSqlSession.class  121行 

    Java代码  收藏代码
    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方法 

    Java代码  收藏代码
    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方法 

    Java代码  收藏代码
    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.   
    8.     // check for nested result maps in parameter mappings (issue #30)  
    9.     for (ParameterMapping pm : boundSql.getParameterMappings()) {  
    10.       String rmId = pm.getResultMapId();  
    11.       if (rmId != null) {  
    12.         ResultMap rm = configuration.getResultMap(rmId);  
    13.         if (rm != null) {  
    14.           hasNestedResultMaps |= rm.hasNestedResultMaps();  
    15.         }  
    16.       }  
    17.     }  
    18.   
    19.     return boundSql;  
    20.   }  



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

    Java代码  收藏代码
    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.   
    10.   BoundSql getBoundSql(Object parameterObject);  
    11.   
    12. }  


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


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


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

    Java代码  收藏代码
    1. public interface SqlNode {  
    2.   boolean apply(DynamicContext context);  
    3. }  


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

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



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

    Java代码  收藏代码
    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 表达式的语法。到这里,上面的问题终于解决了,只需要把代码修改成: 

    Xml代码  收藏代码
    1. <if test='type=="y"'>  
    2.     and status = 0   
    3. </if>  


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

  • 相关阅读:
    一些零碎小知识点积累随笔
    STM32学习笔记——新建工程模板步骤(向原子哥学习)
    记一次电信反射xss的挖掘与利用
    mysql字符串操作相关函数用法总结
    mysql学习笔记
    sigmoid function的直观解释
    多变量线性回归时使用梯度下降(Gradient Descent)求最小值的注意事项
    SAE中Python无法创建多线程的解决方案
    BCNF/3NF 数据库设计范式简介
    web.py下获取get参数
  • 原文地址:https://www.cnblogs.com/dzcWeb/p/7070517.html
Copyright © 2011-2022 走看看