mybatis中的if-test判斷解讀
mybatis if-test判斷解讀
<if test="takeWay == '0'">】mybatis的if判斷
單個的字符要寫到雙引號里面才行,改為<if test='takeWay == "1"'>或者改為<if test="takeWay == '1'.toString() ">
.xml文件的部分代碼
<insert id="insertDelivery" parameterType="com.zuci.request.DeliveryPreferenceReq">
insert cx_customer_deliverypreference
<trim prefix="(" suffix=")" suffixOverrides=",">
.... 此處省略
<if test="takeWay == '1' and workday != null ">
WORKDAY,
</if>
....
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
.... 此處省略
<if test="takeWay == '1' and workday != null ">
#{workday, jdbcType=VARCHAR},
</if>
....
</trim>
</insert>takeWay == “1”處出錯,導(dǎo)致不執(zhí)行if判斷中的sql,運(yùn)行程序不報(bào)錯,沒有任何提示。去掉takeWay == “1” and 則可執(zhí)行。對此我百思不得其解,
因?yàn)樽约河袑戇^如下代碼,是沒錯的。
<if test="messageType == 'senderReceiveSuccess' ">
......
</if>- 把<if test="takeWay == '1' and workday != null ">
- 改為<if test='takeWay == "1" and workday != null '>
- 或改為<if test="takeWay == '1'.toString() and workday != null ">即可。
原因是:mybatis是用OGNL表達(dá)式來解析的,在OGNL的表達(dá)式中,’1’會被解析成字符,java是強(qiáng)類型的,char 和 一個string 會導(dǎo)致不等,所以if標(biāo)簽中的sql不會被解析。
總結(jié)下使用方法:單個的字符要寫到雙引號里面或者使用.toString()才行!
使用Mybatis時(shí),常常會判斷屬性是否為空
- POJO
private Integer status;//狀態(tài),可能為0、1、2、3。
- Mapper XML
<sql>
<trim prefix="where" prefixOverrides="and | or ">
//...省略其他
<if test="status != null and status !=''">and status = #{status}</if>
<trim prefix="where" prefixOverrides="and | or ">
</sql>當(dāng)status的值為 0時(shí)該where SQL and status = 0并未正常拼接,也就是說test內(nèi)的表達(dá)式為false,從而導(dǎo)致查詢結(jié)果錯誤。但是,顯然該值(Integer :0)!= null也!= ' ',應(yīng)該為true才對。
當(dāng)status為Integer類型,并且status值為0時(shí),該if判斷卻為false。
當(dāng)status為0時(shí),Mybatis會解析成'' 空字符串。
為了避免這個問題,改成下面這樣寫,去掉對空字符的判斷,就解決了該問題
<if test="status != null">and status = #{status}</if> 原因分析
通過Debug MyBatis源碼順藤摸瓜找到了IfSqlNode類,該類用來處理動態(tài)SQL的<if>節(jié)點(diǎn),方法public boolean apply(DynamicContext context)用來構(gòu)造節(jié)點(diǎn)內(nèi)的SQL語句。
if (evaluator.evaluateBoolean(test, context.getBindings())
該代碼便是解析
<if test="status !=null and status !=''">
test內(nèi)表達(dá)式的關(guān)鍵,如果表達(dá)式為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 類,發(fā)現(xiàn)解析表達(dá)式使用的是OGNL,如果你使用過古老的Struts框架你應(yīng)該對它不陌生。
通過
OgnlCache.getValue(expression, parameterObject);
可以看到表達(dá)式的值是從緩存中獲取的,由此可知MyBatis竟然對表達(dá)式也做了緩存,以提高性能。
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)去看看,終于找到了解析表達(dá)式的方法
private static Object parseExpression(String expression)
該方法會先從緩存取值,如果沒有便進(jìn)行解析并放入緩存中,然后調(diào)用
Ognl.getValue(parseExpression(expression), root)
獲得表達(dá)式的值。
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)
是如何運(yùn)作的,如果你有興趣可以自行跟下去一探究竟,本文就不贅述了。
到此為止,我們已經(jīng)知道MyBatis的表達(dá)式是用OGNL處理的了,這一點(diǎn)已經(jīng)夠了。
下面我們?nèi)GNL官網(wǎng)看看是不是我們的表達(dá)式語法有問題從而導(dǎo)致該問題的發(fā)生。
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時(shí)將被解析為false,否則為true,浮點(diǎn)型0.00也是如此。
OGNL對于boolean的定義和JavaScript有點(diǎn)像,即'' == 0 == false。
這也就不難理解
<if test="status != null and status !=''">and status = #{status}</if>當(dāng)status=0時(shí)出現(xiàn)的問題了,顯然0!=''是不成立的,導(dǎo)致表達(dá)式的值為false。
將表達(dá)式修改為
<if test="status != null">and status = #{status}</if>該問題便迎刃而解。
該問題的根源還是來自編碼的不規(guī)范,只有String類型才需要判斷是否!='',其他類型完全沒有這個必要,可能是開發(fā)人員為了省事直接復(fù)制上一行拿過來改一改或是所使用的MyBatis生成工具不嚴(yán)謹(jǐn)導(dǎo)致該問題的發(fā)生。
這里有必要再提一個“坑”,如果你有類似于String str ="A"; <if test="str!= null and str == 'A'">這樣的寫法時(shí),你要小心了。
因?yàn)閱我杻?nèi)如果為單個字符時(shí),OGNL將會識別為Java 中的 char類型,顯然String 類型與char類型做==運(yùn)算會返回false,從而導(dǎo)致表達(dá)式不成立。
解決方法很簡單,修改為<if test='str!= null and str == "A"'>即可。
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot加載不出來application.yml文件的解決方法
這篇文章主要介紹了SpringBoot加載不出來application.yml文件的解決方法,文中通過示例代碼講解的非常詳細(xì),對大家的學(xué)習(xí)或者工作有一定的幫助,需要的朋友跟著小編來一起來學(xué)習(xí)吧2023-12-12
Java多線程并發(fā)開發(fā)之DelayQueue使用示例
這篇文章主要為大家詳細(xì)介紹了Java多線程并發(fā)開發(fā)之DelayQueue使用示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
Spring實(shí)戰(zhàn)之Bean的后處理器操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之Bean的后處理器操作,結(jié)合實(shí)例形式詳細(xì)分析了Bean的后處理器相關(guān)配置、操作方法及使用注意事項(xiàng),需要的朋友可以參考下2019-12-12
大模型chat/completions和completions區(qū)別解析
OpenAI的completions和chat/completions是兩個不同的端點(diǎn),completions用于單次文本補(bǔ)全,而chat/completions用于多輪對話生成,選擇哪個端點(diǎn)取決于你的具體需求,本文介紹大模型chat/completions和completions區(qū)別,感興趣的朋友一起看看吧2025-03-03

