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

