JAVA-MyBatis 當(dāng)傳入?yún)?shù)為Integer的0時(shí),動(dòng)態(tài)sql解析為空
之前開發(fā)時(shí)遇到前臺(tái)傳來參數(shù)為0 時(shí),使用mybatis拼接動(dòng)態(tài)sql時(shí)將0解析為空從而跳過if判斷參數(shù)是否為空,無法拼接限制條件的問題,現(xiàn)在深入研究問題的原因
-
現(xiàn)象:
當(dāng)mybatis的xml文件中使用動(dòng)態(tài)sql的if判段,如下:
<if test="warningId !=null and warningId !=''"> and l.warning_id = #{warningId} </if>
warningId為Integer的0時(shí),if判斷會(huì)為false
-
表面原因:
mybatis會(huì)將0解析為 '' 空 warningId!='' 為false跳過拼接條件
-
解決方法:
前臺(tái)處理:將傳入?yún)?shù)改為字符串的0
后臺(tái)處理:判斷條件加上等于0的時(shí)候,如
<if test="warningId !=null and warningId !='' or warningId == 0 " > and l.warning_id = #{warningId} </if>
-
原理及源碼:
查詢資料以及查看mybatis源碼,搞清楚mybatis解析xml文件時(shí)對(duì)if元素的處理
-
當(dāng)mybatis遍歷節(jié)點(diǎn)時(shí),遇到節(jié)點(diǎn)類型為元素,會(huì)使用NodeHandler處理各個(gè)類型的元素,NodeHandler是XMLScriptBuilder的一個(gè)內(nèi)部接口鳖轰,其實(shí)現(xiàn)類包括TrimHandler、WhereHandler焰轻、SetHandler昆雀、IfHandler、ChooseHandler,處理后,會(huì)生成IfSqlNode
private class IfHandler implements NodeHandler { public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { List<SqlNode> contents = parseDynamicTags(nodeToHandle); MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); String test = nodeToHandle.getStringAttribute("test"); IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test); targetContents.add(ifSqlNode); } }
-
當(dāng)處理完所有節(jié)點(diǎn)后,開始構(gòu)造sql
@Override public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context);//此處調(diào)用sqlnode的apply方法 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); context.getBindings().forEach(boundSql::setAdditionalParameter); return boundSql; }
rootSqlNode.apply(context);該處調(diào)用sql的apply方法,然后查看IfSqlNode的結(jié)構(gòu):
public class IfSqlNode implements SqlNode { private final ExpressionEvaluator evaluator; private final String test; private final SqlNode contents; public IfSqlNode(SqlNode contents, String test) { this.test = test; this.contents = contents; this.evaluator = new ExpressionEvaluator(); } @Override public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false; } }
其中appl方法中使用evaluator.evaluateBoolean方法對(duì)表達(dá)式進(jìn)行判斷,evaluator是一個(gè)ExpressionEvaluator 類型,它解析表達(dá)式使用的是OGNL,對(duì)象導(dǎo)航圖語(yǔ)言(Object Graph Navigation Language),簡(jiǎn)稱OGNL已球,是應(yīng)用于Java中的一個(gè)開源的表達(dá)式語(yǔ)言.
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; }
查詢OGNL的官方文檔,
Interpreting Objects as Booleans
Any object can be used where a boolean is required. OGNL interprets objects as booleans like this:
If the object is a
Boolean
, its value is extracted and returned;If the object is a
Number
, its double-precision floating-point value is compared with zero; non-zero is treated astrue
, zero asfalse
;If the object is a
Character
, its boolean value istrue
if and only if its char value is non-zero;-
Otherwise, its boolean value is
true
if and only if it is non-null
.其中說明,如果對(duì)象是一個(gè)Number的類型,值為0時(shí)將被解析為false智亮,否則為true,即'' == 0 == false
-