Mybatis動態(tài)元素if的使用方式
問題
代碼
<if test="status != null and status != ''">
AND status = #{status}
</if>
Mybatis動態(tài)元素if使用,上面寫法適用于status類型為字符串。
若為整型,如int/Integer,當status為0時,就不會進入判斷
解決
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生成
根據斷點進入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;
//由于邏輯關系,只會進入一次
if (args.length == 4) {
//4 個參數時
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 個參數時
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
checkDialectExists();
//對 boundSql 的攔截處理
if (dialect instanceof BoundSqlInterceptor.Chain) {
boundSql = ((BoundSqlInterceptor.Chain) dialect).doBoundSql(BoundSqlInterceptor.Type.ORIGINAL, boundSql, cacheKey);
}
...
進入MappedStatement查看SQL動態(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);這句代碼很關鍵查看IF元素最終執(zhí)行結果
@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)動態(tài)元素節(jié)點
@Override
public boolean apply(DynamicContext context) {
for (SqlNode sqlNode : contents) {
sqlNode.apply(context);
}
return true;
}

進入類IfSqlNode
evaluator.evaluateBoolean(test, context.getBindings())如果為false就不會進入元素判斷中
@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;
}
...
進入類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;
}
關鍵代碼
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時,進不去
結論:
從斷點跟進源碼得出,當!=null條件是,Mybatis會把傳進去的值與null比較,0.0==null為false,當比較空字符""時,會先把空字符進行一次轉化doubleValue(v2),結果為0.0。
所以當status是整型且為0時,就不會進入判斷
重要源碼節(jié)點

SimpleNode::getValue—>SimpleNode::evaluateGetValueBody—>ASTEq::getValueBody—>OgnlOps::equal—>OgnlOps::compareWithConversion
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Springboot jdbctemplate整合實現步驟解析
這篇文章主要介紹了Springboot jdbctemplate整合實現步驟解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
Java中使用JavaMail多發(fā)郵件及郵件的驗證和附件實現
這篇文章主要介紹了Java中使用Java Mail多發(fā)郵件及郵件的驗證和附件實現,包括在郵件中加入圖片等功能的實現講解,需要的朋友可以參考下2016-02-02
詳解Spring注解--@Autowired、@Resource和@Service
本篇文章主要介紹最重要的三個Spring注解,也就是@Autowired、@Resource和@Service,具有很好的參考價值。下面跟著小編一起來看下吧2017-05-05
java同一個類中,一個無事務方法調用一個有事務方法時,事務失效問題
本文詳細介紹了Spring框架中事務管理的實現原理,包括@Transactional注解的使用、事務的開啟、提交和回滾機制,以及代理對象的兩種實現方式(JDK動態(tài)代理和CGLIB代理),文章還探討了在同一個類中調用有事務方法時事務失效的原因,并提供了解決方法2024-12-12

