MyBatis OGNL 表達式的避坑指南
MyBatis 中 OGNL 表達式的那些 “坑”:從字符串比較案例說起
在 MyBatis 開發(fā)中,<if>
標簽的test屬性是實現動態(tài) SQL 的核心,但很多開發(fā)者會遇到 “明明變量值符合預期,條件卻不生效” 的問題。這背后往往是OGNL(Object-Graph Navigation Language)表達式的解析規(guī)則在 “作祟”。本文將通過兩個真實案例,拆解 OGNL 對字符串、字符、數字的處理邏輯,幫你徹底避開這類陷阱。
一、先看兩個 “反直覺” 的案例
在分析原理前,我們先明確核心變量背景:startInstLdgrHierCd是字符串類型,只是不同場景下值不同,卻導致了完全不同的條件判斷結果。
案例 1:變量值為 "0" 時的差異
當startInstLdgrHierCd = "0"(字符串 "0")時,三種寫法結果天差地別:
寫法 | 是否成立 | 疑問 |
---|---|---|
<if test="startInstLdgrHierCd == '0'"> | ? 不成立 | 明明值都是 "0",為什么不生效? |
<if test="startInstLdgrHierCd == 0"> | ? 成立 | 字符串和數字怎么能相等? |
<if test='startInstLdgrHierCd == "0"'> | ? 成立 | 換了引號包裹,為什么又生效了? |
案例 2:變量值為 "01" 時的反轉
當startInstLdgrHierCd = "01"(字符串 "01")時,之前不生效的寫法突然有效了:
<!-- 此時條件成立,與案例1中"0"的情況完全相反 --> <if test="startInstLdgrHierCd == '01'"> AND HQ_INSID = #{startInstId} </if>
這兩個案例的核心矛盾,都指向 OGNL 對 “引號內容的類型解析” 和 “類型比較規(guī)則”—— 這也是 MyBatis 動態(tài) SQL 中最容易踩的坑。
二、OGNL 表達式的核心規(guī)則(必懂)
要解決上述問題,必須先掌握 OGNL 在 MyBatis 中的 3 個關鍵解析邏輯,這是所有判斷的基礎:
1. 單引號' '的解析:字符還是字符串?
OGNL 對單引號內容的判斷,完全取決于字符數量:
單引號內只有「1 個字符」(如'0'、'A')→ 解析為「char 類型(字符)」;
單引號內有「2 個及以上字符」(如'01'、'AB')→ 解析為「String 類型(字符串)」。
這是案例 1 和案例 2 結果差異的核心原因,很多開發(fā)者誤以為 “單引號一定是字符”,實則不然。
2. 雙引號" "的解析:固定為字符串
無論雙引號內有多少個字符(如"0"、"01"),OGNL 都會統(tǒng)一解析為「String 類型(字符串)」。但要注意:XML 屬性值本身需要用引號包裹(單引號或雙引號),因此雙引號的使用會受 XML 語法限制(比如test用雙引號時,內部雙引號會被 XML 解析器當作屬性結束符,導致語法錯誤)。
3. 類型比較規(guī)則:嚴格優(yōu)先,自動轉換為輔
OGNL 比較兩個值時,遵循 “先看類型,再看值” 的邏輯:
若類型相同:直接比較值是否相等(如 String 與 String、int 與 int);
若類型不同:僅在 “字符串與數字” 之間會嘗試自動類型轉換(字符串轉數字),其他類型組合(如 String 與 char)直接判定為不相等。
三、逐案拆解:為什么結果不一樣?
結合上述規(guī)則,我們重新分析兩個案例,所有 “反直覺” 的現象都會迎刃而解。
案例 1:變量值為 "0"(字符串)的三種寫法
變量startInstLdgrHierCd的類型是 String,值為 "0",我們逐一拆解三種寫法的判斷邏輯:
寫法 1: → 不成立
- 右邊'0':1 個字符 → OGNL 解析為「char 類型」;
- 左邊變量:String 類型;
- 比較邏輯:String 與 char 類型不同,且 OGNL 不支持這兩種類型的自動轉換 → 直接判定為不相等(結果為 false)。
類比 Java 代碼:String a = "0"; char b = '0'; a == b → 結果 false。
寫法 2: → 成立
- 右邊0:無引號 → OGNL 解析為「int 類型(數字)」;
- 左邊變量:String 類型;
- 比較邏輯:String 與 int 類型不同,但 OGNL 支持 “字符串轉數字” 的自動轉換 → 字符串 "0" 轉成數字 0 后,與右邊的 0 相等(結果為 true)。
類比 Java 代碼:String a = "0"; int b = 0; Integer.parseInt(a) == b → 結果 true。
寫法 3: → 成立
- 外層test用單引號包裹:內部的"0"不會被 XML 解析器誤判,OGNL 正常解析為「String 類型」;
- 右邊"0":雙引號 → 解析為 String 類型;
- 左邊變量:String 類型;
- 比較邏輯:類型相同(均為 String),值均為 "0" → 判定為相等(結果為 true)。
案例 2:變量值為 "01"(字符串)的寫法
變量startInstLdgrHierCd的類型是 String,值為 "01",分析<if test="startInstLdgrHierCd == '01'">
:
- 右邊'01':2 個字符 → OGNL 解析為「String 類型」;
- 左邊變量:String 類型;
- 比較邏輯:類型相同(均為 String),值均為 "01" → 判定為相等(結果為 true)。
這就解釋了為什么 “同樣是單引號”,值為 "0" 時不生效,值為 "01" 時卻生效 —— 核心是單引號內字符數量改變了解析后的類型。
四、避坑指南:MyBatis OGNL 的最佳實踐
通過以上分析,我們可以總結出 3 條實用規(guī)則,徹底避免 OGNL 表達式的類型混淆問題:
1. 字符串比較:統(tǒng)一用 “單引號包 test,雙引號包值”
這是最安全的寫法,能明確指定兩邊都是 String 類型,完全避開單引號解析的陷阱:
<!-- 推薦寫法:test用單引號,值用雙引號 --> <if test='startInstLdgrHierCd == "0"'> <if test='startInstLdgrHierCd == "01"'>
2. 避免 “字符串與數字” 的直接比較
雖然 OGNL 支持字符串轉數字,但這種自動轉換存在風險(比如字符串無法轉數字時會報錯,如"A" == 0會拋出轉換異常)。若業(yè)務需要比較數字,建議先將變量轉為數字類型(如在 Java 代碼中處理),再在test中比較:
// 錯誤:直接用字符串比較數字 String startInstLdgrHierCd = "0"; // 正確:先轉為數字 Integer ldgrHierCd = Integer.parseInt(startInstLdgrHierCd); param.put("ldgrHierCd", ldgrHierCd);
<!-- 此時兩邊都是int類型,無轉換風險 --> <if test="ldgrHierCd == 0">
3. 單引號僅用于 “單個字符” 的比較(謹慎使用)
若確實需要比較 char 類型(如變量是 Character 類型),再用單引號,且務必確保值是單個字符:
<!-- 變量是Character類型時才推薦 --> <if test="charVar == 'Y'">
五、總結
MyBatis 的 OGNL 表達式看似簡單,實則暗藏 “類型解析” 的細節(jié)。很多開發(fā)者踩坑的本質,是忽略了 “單引號的字符數量影響類型” 和 “OGNL 的自動轉換規(guī)則”。
記住核心結論:
- 單引號內 1 個字符→char,≥2 個字符→String;
- 雙引號永遠是 String,推薦用test='變量 == "值"';
- 避免字符串與數字直接比較,優(yōu)先統(tǒng)一類型。
掌握這些規(guī)則后,你就能輕松應對 MyBatis 動態(tài) SQL 的各種條件判斷,再也不用為 “條件不生效” 而頭疼了!
到此這篇關于MyBatis OGNL 表達式的避坑指南的文章就介紹到這了,更多相關MyBatis OGNL 表達式內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot @Cacheable自定義KeyGenerator方式
這篇文章主要介紹了SpringBoot @Cacheable自定義KeyGenerator方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12Java中的字節(jié)流InputStream和OutputStream詳解
這篇文章主要介紹了Java中的字節(jié)流InputStream和OutputStream詳解,繼承自InputStream的流都是用于向程序中輸入數據,且數據的單位為字節(jié)8bit,我們看到的具體的某一些管道,凡是以InputStream結尾的管道,都是以字節(jié)的形式向我們的程序輸入數據,需要的朋友可以參考下2023-10-10