轉自:http://blog.csdn.net/z69183787/article/details/51589171
最近在項目使用mybatis中碰到個問題?
Xml代碼??
<if?test="type=='y'">?? ????and?status?=?0??? </if>??
當傳入的type的值為y的時候,if判斷內的sql也不會執行,抱著這個疑問就去看了mybatis是怎么解析sql的。下面我們一起來看一下mybatis 的執行過程。?
DefaultSqlSession.class? 121行?
Java代碼??
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方法?
Java代碼??
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方法?
Java代碼??
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);?? ????}?? ?? ?????? ????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是一個接口。?
Java代碼??
? ? ? ? ? ? ?? public?interface?SqlSource?{?? ?? ??BoundSql?getBoundSql(Object?parameterObject);?? ?? }??
類中getBoundSql是一個核心方法,mybatis 也是通過這個方法來為我們構建sql。BoundSql 對象其中保存了經過參數解析,以及判斷解析完成sql語句。比如<if> <choose> <when> 都回在這一層完成,具體的完成方法往下看,那最常用sqlSource的實現類是DynamicSqlSource.class?
Java代碼??
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是一個接口?
Java代碼??
public?interface?SqlNode?{?? ??boolean?apply(DynamicContext?context);?? }??
可以看到類中 rootSqlNode.apply(context); 的方法執行就是一個遞歸的調用,通過不同的?
實現類執行不同的標簽,每一次appll是完成了我們<></>一次標簽中的sql創建,計算出標簽中的那一段sql,mybatis通過不停的遞歸調用,來為我們完成了整個sql的拼接。那我們主要來看IF的實現類IfSqlNode.class?
Java代碼??
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 的方法?
Java代碼??
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 表達式的語法。到這里,上面的問題終于解決了,只需要把代碼修改成:?
Xml代碼??
<if?test='type=="y"'>?? ????and?status?=?0??? </if>??
就可以執行了,這樣"y"解析出來是一個字符串,兩者相等!
總結
以上是生活随笔為你收集整理的【mybatis】IF判断的坑 (实现if test= status == 'zhangsan')的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。