溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

mybatis?if?test條件判斷語句中的判斷問題實(shí)例分析

發(fā)布時(shí)間:2022-03-14 09:24:42 來源:億速云 閱讀:380 作者:iii 欄目:開發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“mybatis if test條件判斷語句中的判斷問題實(shí)例分析”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“mybatis if test條件判斷語句中的判斷問題實(shí)例分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識(shí)吧。

if test條件判斷語句中的判斷問題

寫這個(gè)主要是描述在mybatis中要注意的問題,很不幸,自己沒注意,跳坑了。

我在mybatis中定義的sql語句如下

<if test="facilityOccupied != null and facilityOccupied != '' and facilityOccupied == 1 ">
                and z.serviceCount = 1
            </if>    
            <if test="facilityOccupied != null and facilityOccupied != '' and facilityOccupied == 0 ">
                and z.serviceCount = 0
            </if>

可以看到這里只是對(duì)傳入?yún)?shù)一個(gè)簡(jiǎn)單的判斷。

controller層傳入一個(gè)Integer類型的facilityOccupied參數(shù)。

表面上看沒什么問題,當(dāng)傳入facilityOccupied = 1,測(cè)試結(jié)果很意外,它查詢了所有的結(jié)果,也就是說它沒有符合這個(gè)判斷facilityOccupied == 1 。

換一種寫法,在controller層中將傳入?yún)?shù)facilityOccupied改為String類型的,通過查詢結(jié)果可以看出它符合facilityOccupied == 1 這個(gè)判斷條件。

或使用equals()

<if test="facilityOccupied != null and facilityOccupied != '' and facilityOccupied.equals(1)">
                and z.serviceCount = 1
            </if>    
            <if test="facilityOccupied != null and facilityOccupied != '' and facilityOccupied.equals(0)">
                and z.serviceCount = 0
            </if>

所以,總結(jié)起來怎么說,在這個(gè)地方比較的并不是數(shù)值大小,而是物理地址,這個(gè)雙引號(hào)里面的1不是int類型也不是integer類型,而是String字符串類型,啊多么痛的領(lǐng)悟。

補(bǔ)充一下,test中eq 和 == 效果一樣,比較的是地址,所以比較值最好使用equals()。

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)樽约河袑戇^如下代碼,是沒錯(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á)式來解析的,在OGNL的表達(dá)式中,&rsquo;1&rsquo;會(huì)被解析成字符,java是強(qiáng)類型的,char 和 一個(gè)string 會(huì)導(dǎo)致不等,所以if標(biāo)簽中的sql不會(huì)被解析。

總結(jié)下使用方法:單個(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并未正常拼接,也就是說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è)問題,改成下面這樣寫,去掉對(duì)空字符的判斷,就解決了該問題

<if test="status != null">and status = #{status}</if>

原因分析

通過Debug MyBatis源碼順藤摸瓜找到了IfSqlNode類,該類用來處理動(dòng)態(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)該對(duì)它不陌生。通過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á)式語法有問題從而導(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.

果然,如果對(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)的問題了,顯然0!=''是不成立的,導(dǎo)致表達(dá)式的值為false。

將表達(dá)式修改為<if test="status != null">and status = #{status}</if>該問題便迎刃而解。該問題的根源還是來自編碼的不規(guī)范,只有String類型才需要判斷是否!='',其他類型完全沒有這個(gè)必要,可能是開發(fā)人員為了省事直接復(fù)制上一行拿過來改一改或是所使用的MyBatis生成工具不嚴(yán)謹(jǐn)導(dǎo)致該問題的發(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"'>即可。

讀到這里,這篇“mybatis if test條件判斷語句中的判斷問題實(shí)例分析”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI