Mybatis動(dòng)態(tài)元素if的使用方式
問題
代碼
<if test="status != null and status != ''"> AND status = #{status} </if>
Mybatis動(dòng)態(tài)元素if使用,上面寫法適用于status類型為字符串。
若為整型,如int/Integer,當(dāng)status為0時(shí),就不會(huì)進(jìn)入判斷
解決
1:
<if test="status != null and status != '' or status == 0"> AND u.status = #{status} </if>
2
<if test="status != null"> AND u.status = #{status} </if>
源碼閱讀
查看SQL生成
根據(jù)斷點(diǎn)進(jìn)入PageInterceptor
@Override public Object intercept(Invocation invocation) throws Throwable { try { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; Executor executor = (Executor) invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; //由于邏輯關(guān)系,只會(huì)進(jìn)入一次 if (args.length == 4) { //4 個(gè)參數(shù)時(shí) boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { //6 個(gè)參數(shù)時(shí) cacheKey = (CacheKey) args[4]; boundSql = (BoundSql) args[5]; } checkDialectExists(); //對(duì) boundSql 的攔截處理 if (dialect instanceof BoundSqlInterceptor.Chain) { boundSql = ((BoundSqlInterceptor.Chain) dialect).doBoundSql(BoundSqlInterceptor.Type.ORIGINAL, boundSql, cacheKey); } ...
進(jìn)入MappedStatement查看SQL動(dòng)態(tài)綁定
public BoundSql getBoundSql(Object parameterObject) { BoundSql boundSql = sqlSource.getBoundSql(parameterObject); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings == null || parameterMappings.isEmpty()) { boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); } ...
rootSqlNode.apply(context);這句代碼很關(guān)鍵查看IF元素最終執(zhí)行結(jié)果
@Override 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; }
循環(huán)動(dòng)態(tài)元素節(jié)點(diǎn)
@Override public boolean apply(DynamicContext context) { for (SqlNode sqlNode : contents) { sqlNode.apply(context); } return true; }
進(jìn)入類IfSqlNode
evaluator.evaluateBoolean(test, context.getBindings())如果為false就不會(huì)進(jìn)入元素判斷中
@Override public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false; }
判斷邏輯
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; } ...
進(jìn)入類OgnlCache
public static Object getValue(String expression, Object root) { try { Map<Object, OgnlClassResolver> context = Ognl.createDefaultContext(root, new OgnlClassResolver()); return Ognl.getValue(parseExpression(expression), context, root); } catch (OgnlException e) { throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e); } }
protected Object evaluateGetValueBody(OgnlContext context, Object source) throws OgnlException { context.setCurrentObject(source); context.setCurrentNode(this); if (!this._constantValueCalculated) { this._constantValueCalculated = true; boolean constant = this.isConstant(context); if (constant) { this._constantValue = this.getValueBody(context, source); } this._hasConstantValue = constant; } return this._hasConstantValue ? this._constantValue : this.getValueBody(context, source); }
protected Object getValueBody(OgnlContext context, Object source) throws OgnlException { Object v1 = this._children[0].getValue(context, source); Object v2 = this._children[1].getValue(context, source); return OgnlOps.equal(v1, v2) ? Boolean.FALSE : Boolean.TRUE; }
public static boolean equal(Object v1, Object v2) { if (v1 == null) { return v2 == null; } else if (v1 != v2 && !isEqual(v1, v2)) { if (v1 instanceof Number && v2 instanceof Number) { return ((Number)v1).doubleValue() == ((Number)v2).doubleValue(); } else { return false; } } else { return true; } }
public static boolean isEqual(Object object1, Object object2) { boolean result = false; if (object1 == object2) { result = true; } else if (object1 != null && object1.getClass().isArray()) { if (object2 != null && object2.getClass().isArray() && object2.getClass() == object1.getClass()) { result = Array.getLength(object1) == Array.getLength(object2); if (result) { int i = 0; for(int icount = Array.getLength(object1); result && i < icount; ++i) { result = isEqual(Array.get(object1, i), Array.get(object2, i)); } } } } else { result = object1 != null && object2 != null && (object1.equals(object2) || compareWithConversion(object1, object2) == 0); } return result; }
關(guān)鍵代碼
public static int compareWithConversion(Object v1, Object v2) { int result; if (v1 == v2) { result = 0; } else { int t1 = getNumericType(v1); int t2 = getNumericType(v2); int type = getNumericType(t1, t2, true); switch(type) { case 6: result = bigIntValue(v1).compareTo(bigIntValue(v2)); break; case 9: result = bigDecValue(v1).compareTo(bigDecValue(v2)); break; case 10: if (t1 == 10 && t2 == 10) { if (v1 instanceof Comparable && v1.getClass().isAssignableFrom(v2.getClass())) { result = ((Comparable)v1).compareTo(v2); break; } throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and " + v2.getClass().getName()); } case 7: case 8: double dv1 = doubleValue(v1); double dv2 = doubleValue(v2); return dv1 == dv2 ? 0 : (dv1 < dv2 ? -1 : 1); default: long lv1 = longValue(v1); long lv2 = longValue(v2); return lv1 == lv2 ? 0 : (lv1 < lv2 ? -1 : 1); } } return result; }
public static double doubleValue(Object value) throws NumberFormatException { if (value == null) { return 0.0D; } else { Class c = value.getClass(); if (c.getSuperclass() == Number.class) { return ((Number)value).doubleValue(); } else if (c == Boolean.class) { return (Boolean)value ? 1.0D : 0.0D; } else if (c == Character.class) { return (double)(Character)value; } else { // 原因就這了?。?! String s = stringValue(value, true); return s.length() == 0 ? 0.0D : Double.parseDouble(s); } } }
為什么為0時(shí),進(jìn)不去
結(jié)論:
從斷點(diǎn)跟進(jìn)源碼得出,當(dāng)!=null條件是,Mybatis會(huì)把傳進(jìn)去的值與null比較,0.0==null為false,當(dāng)比較空字符""時(shí),會(huì)先把空字符進(jìn)行一次轉(zhuǎn)化doubleValue(v2),結(jié)果為0.0。
所以當(dāng)status是整型且為0時(shí),就不會(huì)進(jìn)入判斷
重要源碼節(jié)點(diǎn)
SimpleNode::getValue—>SimpleNode::evaluateGetValueBody—>ASTEq::getValueBody—>OgnlOps::equal—>OgnlOps::compareWithConversion
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java使用itextpdf實(shí)現(xiàn)Excel轉(zhuǎn)PDF
這篇文章主要為大家詳細(xì)介紹了Java如何使用itextpdf實(shí)現(xiàn)Excel轉(zhuǎn)PDF,并且支持xlsx和xls兩種格,文中的示例代碼講解詳細(xì),希望對(duì)大家有所幫助2024-01-01Springboot jdbctemplate整合實(shí)現(xiàn)步驟解析
這篇文章主要介紹了Springboot jdbctemplate整合實(shí)現(xiàn)步驟解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08SpringBoot集成極光推送完整實(shí)現(xiàn)代碼
本文主要介紹了SpringBoot集成極光推送完整實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Java中使用JavaMail多發(fā)郵件及郵件的驗(yàn)證和附件實(shí)現(xiàn)
這篇文章主要介紹了Java中使用Java Mail多發(fā)郵件及郵件的驗(yàn)證和附件實(shí)現(xiàn),包括在郵件中加入圖片等功能的實(shí)現(xiàn)講解,需要的朋友可以參考下2016-02-02JDBC獲取數(shù)據(jù)庫連接的5種方式實(shí)例
JDBC是一種用于執(zhí)行SQL語句的JavaAPI,為多種關(guān)系數(shù)據(jù)庫提供統(tǒng)一訪問,它由一組用Java語言編寫的類和接口組成,提供了諸如查詢和更新數(shù)據(jù)庫中數(shù)據(jù)的方法,這篇文章主要給大家介紹了關(guān)于JDBC獲取數(shù)據(jù)庫連接的5種方式,需要的朋友可以參考下2022-06-06詳解Spring注解--@Autowired、@Resource和@Service
本篇文章主要介紹最重要的三個(gè)Spring注解,也就是@Autowired、@Resource和@Service,具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-05-05java同一個(gè)類中,一個(gè)無事務(wù)方法調(diào)用一個(gè)有事務(wù)方法時(shí),事務(wù)失效問題
本文詳細(xì)介紹了Spring框架中事務(wù)管理的實(shí)現(xiàn)原理,包括@Transactional注解的使用、事務(wù)的開啟、提交和回滾機(jī)制,以及代理對(duì)象的兩種實(shí)現(xiàn)方式(JDK動(dòng)態(tài)代理和CGLIB代理),文章還探討了在同一個(gè)類中調(diào)用有事務(wù)方法時(shí)事務(wù)失效的原因,并提供了解決方法2024-12-12