mybatis----Integer = 0 刷選不出來條件原因以及sql改法
Xml寫法:
POJO:
當status
的值為 0時該where SQLand status = 0
並未正常拼接,也就是說test內的表達式為false
,從而導致查詢結果錯誤。但是,顯然該值(Integer :0)!= null也!= ‘ ‘,應該為true
才對。
通過Debug MyBatis源碼順藤摸瓜找到了IfSqlNode
類,該類用來處理動態SQL的<if>節點,方法public boolean apply(DynamicContext context)
用來構造節點內的SQL語句。if (evaluator.evaluateBoolean(test, context.getBindings())
<if test="status !=null and status !=‘‘">
test內表達式的關鍵,如果表達式為true則拼接SQL,否則忽略。
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; } }
打開ExpressionEvaluator 類,發現解析表達式使用的是OGNL,如果你使用過古老的Struts框架你應該對它不陌生。通過
OgnlCache.getValue(expression, parameterObject);
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; }跟進去看看,終於找到了解析表達式的方法
private static Object parseExpression(String expression)
,該方法會先從緩存取值,如果沒有便進行解析並放入緩存中,然後調用Ognl.getValue(parseExpression(expression), root)
獲得表達式的值。
public class OgnlCache { private static final Map<String, ognl.Node> expressionCache = new ConcurrentHashMap<String, ognl.Node>(); public static Object getValue(String expression, Object root) throws OgnlException { return Ognl.getValue(parseExpression(expression), root); } private static Object parseExpression(String expression) throws OgnlException { try { Node node = expressionCache.get(expression); if (node == null) { node = new OgnlParser(new StringReader(expression)).topLevelExpression(); expressionCache.put(expression, node); } return node; } catch (ParseException e) { throw new ExpressionSyntaxException(expression, e); } catch (TokenMgrError e) { throw new ExpressionSyntaxException(expression, e); } }至於
Ognl.getValue(parseExpression(expression), root)
是如何運作的,如果你有興趣可以自行跟下去一探究竟,本文就不贅述了。到此為止,我們已經知道MyBatis的表達式是用OGNL處理的了,這一點已經夠了。下面我們去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 as true, zero as false;
- If the object is a Character, its boolean value is true if and only if its char value is non-zero;
- Otherwise, its boolean value is true if and only if it is non-null.
果然,如果對象是一個Number類型,值為0時將被解析為false
,否則為true
,浮點型0.00也是如此。OGNL對於boolean的定義和JavaScript有點像,即‘‘ == 0 == false
。這也就不難理解<if test="status != null and status !=‘‘">and status = #{status}</if>
當status=0時出現的問題了,顯然0!=‘‘
是不成立的,導致表達式的值為false。
將表達式修改為<if test="status != null">and status = #{status}</if>
該問題便迎刃而解。該問題的根源還是來自編碼的不規範,只有String類型才需要判斷是否!=‘‘
,其他類型完全沒有這個必要,可能是開發人員為了省事直接復制上一行拿過來改一改或是所使用的MyBatis生成工具不嚴謹導致該問題的發生。
這裏有必要再提一個“坑”,如果你有類似於String str ="A";
<if test="str!= null and str == ‘A‘">
這樣的寫法時,你要小心了。因為單引號內如果為單個字符時,OGNL將會識別為Java 中的 char類型,顯然String 類型與char類型做==
運算會返回false
,從而導致表達式不成立。解決方法很簡單,修改為<if test=‘str!= null and str == "A"‘>
即
mybatis----Integer = 0 刷選不出來條件原因以及sql改法