mybatis中的if-test判斷解讀
mybatis if-test判斷解讀
<if test="takeWay == '0'">】mybatis的if判斷
單個(gè)的字符要寫到雙引號(hào)里面才行,改為<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”處出錯(cuò),導(dǎo)致不執(zhí)行if判斷中的sql,運(yùn)行程序不報(bào)錯(cuò),沒有任何提示。去掉takeWay == “1” and 則可執(zhí)行。對(duì)此我百思不得其解,
因?yàn)樽约河袑戇^(guò)如下代碼,是沒錯(cuò)的。
<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á)式來(lái)解析的,在OGNL的表達(dá)式中,’1’會(huì)被解析成字符,java是強(qiáng)類型的,char 和 一個(gè)string 會(huì)導(dǎo)致不等,所以if標(biāo)簽中的sql不會(huì)被解析。
總結(jié)下使用方法:?jiǎn)蝹€(gè)的字符要寫到雙引號(hào)里面或者使用.toString()才行!
使用Mybatis時(shí),常常會(huì)判斷屬性是否為空
- 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
并未正常拼接,也就是說(shuō)test內(nèi)的表達(dá)式為false
,從而導(dǎo)致查詢結(jié)果錯(cuò)誤。但是,顯然該值(Integer :0)!= null也!= ' ',應(yīng)該為true
才對(duì)。
當(dāng)status為Integer類型,并且status值為0時(shí),該if判斷卻為false。
當(dāng)status為0時(shí),Mybatis會(huì)解析成'' 空字符串。
為了避免這個(gè)問(wèn)題,改成下面這樣寫,去掉對(duì)空字符的判斷,就解決了該問(wèn)題
<if test="status != null">and status = #{status}</if>
原因分析
通過(guò)Debug MyBatis源碼順藤摸瓜找到了IfSqlNode
類,該類用來(lái)處理動(dòng)態(tài)SQL的<if>節(jié)點(diǎn),方法public boolean apply(DynamicContext context)
用來(lái)構(gòu)造節(jié)點(diǎn)內(nèi)的SQL語(yǔ)句。
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,如果你使用過(guò)古老的Struts框架你應(yīng)該對(duì)它不陌生。
通過(guò)
OgnlCache.getValue(expression, parameterObject);
可以看到表達(dá)式的值是從緩存中獲取的,由此可知MyBatis竟然對(duì)表達(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)
該方法會(huì)先從緩存取值,如果沒有便進(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á)式語(yǔ)法有問(wèn)題從而導(dǎo)致該問(wèn)題的發(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.
果然,如果對(duì)象是一個(gè)Number類型,值為0時(shí)將被解析為false
,否則為true
,浮點(diǎn)型0.00也是如此。
OGNL對(duì)于boolean的定義和JavaScript有點(diǎn)像,即'' == 0 == false
。
這也就不難理解
<if test="status != null and status !=''">and status = #{status}</if>
當(dāng)status=0時(shí)出現(xiàn)的問(wèn)題了,顯然0!=''
是不成立的,導(dǎo)致表達(dá)式的值為false。
將表達(dá)式修改為
<if test="status != null">and status = #{status}</if>
該問(wèn)題便迎刃而解。
該問(wèn)題的根源還是來(lái)自編碼的不規(guī)范,只有String類型才需要判斷是否!=''
,其他類型完全沒有這個(gè)必要,可能是開發(fā)人員為了省事直接復(fù)制上一行拿過(guò)來(lái)改一改或是所使用的MyBatis生成工具不嚴(yán)謹(jǐn)導(dǎo)致該問(wèn)題的發(fā)生。
這里有必要再提一個(gè)“坑”,如果你有類似于String str ="A";
<if test="str!= null and str == 'A'">
這樣的寫法時(shí),你要小心了。
因?yàn)閱我?hào)內(nèi)如果為單個(gè)字符時(shí),OGNL將會(huì)識(shí)別為Java 中的 char類型,顯然String 類型與char類型做==
運(yùn)算會(huì)返回false
,從而導(dǎo)致表達(dá)式不成立。
解決方法很簡(jiǎn)單,修改為<if test='str!= null and str == "A"'>
即可。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺談異常結(jié)構(gòu)圖、編譯期異常和運(yùn)行期異常的區(qū)別
下面小編就為大家?guī)?lái)一篇淺談異常結(jié)構(gòu)圖、編譯期異常和運(yùn)行期異常的區(qū)別。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09如何通過(guò)Java監(jiān)聽MySQL數(shù)據(jù)的變化
對(duì)于二次開發(fā)來(lái)說(shuō),很大一部分就找找文件和找數(shù)據(jù)庫(kù)的變化情況,下面這篇文章主要給大家介紹了關(guān)于如何通過(guò)Java監(jiān)聽MySQL數(shù)據(jù)的變化的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03Java內(nèi)存結(jié)構(gòu)和數(shù)據(jù)類型
本文重點(diǎn)給大家介紹java內(nèi)存結(jié)構(gòu)和數(shù)據(jù)類型知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2016-12-12maven插件maven-jar-plugin構(gòu)建jar文件的詳細(xì)使用
maven-jar-plugin插件時(shí)maven中最常用的插件,也是maven構(gòu)建Java程序執(zhí)行包或者依賴包的默認(rèn)插件,本文主要介紹了maven插件maven-jar-plugin構(gòu)建jar文件的詳細(xì)使用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之背包問(wèn)題
背包問(wèn)題是一個(gè)非常典型的考察動(dòng)態(tài)規(guī)劃應(yīng)用的題目,對(duì)其加上不同的限制和條件,可以衍生出諸多變種,若要全面理解動(dòng)態(tài)規(guī)劃,就必須對(duì)背包問(wèn)題了如指掌2022-02-02透明化Sharding-JDBC數(shù)據(jù)庫(kù)字段加解密方案
這篇文章主要為大家介紹了透明化Sharding-JDBC數(shù)據(jù)庫(kù)字段加解密方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-02-02java.lang.AbstractMethodError: org.apache.xerces.dom.Documen
這篇文章主要介紹了java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.setXmlVersion問(wèn)題解決方法,導(dǎo)致本文問(wèn)題的原因是缺少一個(gè)xerces.jar jar包,需要的朋友可以參考下2015-03-03