if
if 標(biāo)簽通常用于 WHE阻 語(yǔ)句中,通過(guò)判斷參數(shù)值來(lái)決定是否使用某個(gè)查詢條件,它也經(jīng)常用于 UPDATE 語(yǔ)句中判斷是否更新某一個(gè)字段 , 還可以在 INSERT 語(yǔ)句中用來(lái)判斷是否插入某個(gè)字段的值。
if 標(biāo)簽有一個(gè)必填的屬性 test, test 的屬性值是一個(gè)符合 OGNL 要求的判斷表達(dá)式,表達(dá)式的結(jié)果可以是 true 或 false ,除此之外所有的非 0值都為 true ,只有0為 false 。 為了方便理解,在表達(dá)式中 ,建議只用 true 或 false 作為結(jié)果 。
- 判斷條件 property ! =nu ll 或 property == null : 適用于任何類型的宇段 ,用于判斷屬性值是否為空。
- 判斷條件 property != '' 或 prope rty == '': 僅適用于 String 類型的宇段 ,用于判斷是否為空字符串 。
- and 和 or :當(dāng)有多個(gè)判斷條件時(shí),使用 and 或 or 進(jìn)行連接,嵌套的判斷可以使用小括號(hào)分組, and 相當(dāng)于
Java 中的與(&&), or 相當(dāng)于 Java 中的或 (||)。
上面兩個(gè)條件的屬性類型都是 String,對(duì)字符串的判斷和 Java 中的判斷類似,首先需要判斷字段是否為 null ,然后再去判斷是否為空(在 OGNL 表達(dá)式中 ,這兩個(gè)判斷的順序不會(huì)影響判斷的結(jié)果,也不會(huì)有空指針異常)。在本章所有例子中,字符串的判斷幾乎都包含 null和空的判斷,這兩個(gè)條件不是必須寫在一起,可以根據(jù)實(shí)際業(yè)務(wù)決定是否需要空值判斷 。
choose when otherwise
if 標(biāo)簽提供了基本的條件判斷,但是它無(wú)法實(shí)現(xiàn) if. . . else 、 if ... else ...的邏輯,要想實(shí)現(xiàn)這樣的邏輯,就需用到 choose when otherwise 標(biāo)簽。
choose 元素中包含 when和 otherwise 兩個(gè)標(biāo)簽,一個(gè) choose 中至少有一個(gè) when ,有 0 個(gè)或者1 個(gè)
otherwise 。在己有的 sys_user 表中,除了主鍵 id 外,我們認(rèn)為 user_name (用戶名)也是唯一的,所有的用戶名都不可以重復(fù) 。 現(xiàn)在進(jìn)行如下查詢:
當(dāng)參數(shù) id 有值的時(shí)候優(yōu)先使用 id 查詢,當(dāng) id 沒(méi)有值時(shí)就去判斷用戶名是否有值,如果有值就用用戶名查詢 ,如果用 戶名也沒(méi)有值,就使 SQL 查詢無(wú)結(jié)果 。
<select id=” selectByidOrUserName” resultType=” tk.mybatis.simple.m0del. SysUser” >
select id,
user_name,
user_password,
user_email,
user_info
from sys_ user
where 1 = 1
<choose >
<when test=” id != null ” >
and id= #{id}
</when >
<when test=” u serName != null and userName !=””>
and user name = #{userName}
</when>
<otherwise>
and 1 = 2
</otherwise>
</choose>
</ select>
在以上查詢中,如果沒(méi)有 otherwise 這個(gè)限制條件,所有的用戶都會(huì)被查詢出來(lái),因?yàn)?br>
我們?cè)趯?duì)應(yīng) 的接口方法中使用了 Sys User 作為返回值,所以當(dāng)實(shí)際查詢結(jié)果是多個(gè)時(shí)就會(huì)報(bào)
錯(cuò)。 添加 otherwise 條件后,由于 where 條件不滿足,因此在這種情況下就查詢不到結(jié)果。
where
這 3 個(gè)標(biāo)簽解決了類似的問(wèn)題,并且 where 和 set 都屬于 trim 的一種具體用法。下面分別來(lái)看這 3 個(gè)標(biāo)簽。
where 標(biāo)簽的作用:如果該標(biāo)簽包含的元素中有返回值,就插入一個(gè) where ;如果 where后面的字符串是以 AND 和 OR 開(kāi)頭的,就將它們剔除。
<select id=” selectByUser” resultType=” tk . mybatis . simple . model . SysUser” >
*
from sys_user
<where>
<if test=”userName != null and userName !=””>
and user_name like concat ( ’ %’,#{userName} , ’ % ’)
</ if>
<if test=”userEmail ! = ” and userEmail != null ”>
and user email = #{userEmail}
</ if>
</where>
</select >
當(dāng) if 條件都不滿足的時(shí)候, where 元素中沒(méi)有內(nèi)容,所以在 SQL 中不會(huì)出現(xiàn) where , 如果 if 條件滿足, where 元素的內(nèi)容就是以 and 開(kāi)頭的條件, where 會(huì)自動(dòng)去掉開(kāi)頭的 and,這也能保證 where 條件正確 。這種情況下生成的 SQL 更干凈、更貼切,不會(huì)在任何情況下都有 where 1 = 1 這樣的條件 。
set
set 標(biāo)簽的作用:如果該標(biāo)簽包含的元素中有返回值,就插入一個(gè) set :如果 set 后面的字符串是 以逗號(hào)結(jié)尾的,就將這個(gè)逗號(hào)剔除 。
<update id=”updateByidSelective ” >
update sys_user
<set>
<if test=” userName != null and userName !=””>
user name= #{userName} ,
</ if>
<if test=” userPassword != null and userPassword ! = ””>
user password= #{userPassword} ,
</if>
<if test=”userEmail != null and userEmail != ””>
user email = #{userEmail} ,
</ if>
<i f test=” userinfo != null and userinfo !=””>
user info = #{userinfo},
</if>
<if test=”headimg != null ” >
head_img = #{headimg, jdbcType=BLOB},
</if >
<if test=”createTime != nul l ”>
create_time = #{createTime, jdbcType=TIMESTAMP},
</if>
id = # {id} ,
</set>
where id = #{id}
</ update>
在 set 標(biāo)簽的用法中 , SQL 后面的逗號(hào)沒(méi)有問(wèn)題了,但是如果 set 元素中沒(méi)有內(nèi)容,照樣
會(huì)出現(xiàn) SQL 錯(cuò)誤,所以為了避免錯(cuò)誤產(chǎn)生,類似 id = #{id }這樣必然存在的賦值仍然有保留
的必要。從這一點(diǎn)來(lái)看, set 標(biāo)簽并沒(méi)有解決全部的問(wèn)題,使用時(shí)仍然需要注意。
trim
where 和 set 標(biāo)簽 的功能都可以用 trim 標(biāo)簽來(lái)實(shí)現(xiàn),并且在底層就是通過(guò)TrimSqlNode 實(shí)現(xiàn)的 。
where 標(biāo)簽對(duì)應(yīng) trim 的實(shí)現(xiàn)如下。
<trim prefix=”WHERE ” prefixOverrides=”AND IOR ” >
......
</ trim>
這里的 AND 和 OR 后面的空格不能省略,為了避免匹配到 andes 、 orders 等單詞 。
實(shí)際的 prefixeOverrides 包含“AND”、“OR”、“AND\n ”、“OR\n ”、“AND\r”、OR\r”、“AND\t ”、 “ OR \t ”, 不僅僅是上面提到的兩個(gè)帶空格的前綴 。
set 標(biāo)簽對(duì)應(yīng) 的 trim 實(shí)現(xiàn)如下 。
<trim prefix=” SET” suf f ixOverrides=”, ” >
.....
</ trim>
trim 標(biāo)簽有如下屬性。
prefix :當(dāng) trim 元素內(nèi)包含內(nèi)容時(shí),會(huì)給內(nèi)容增加 prefix 指定的前綴。
prefixOverrides :當(dāng) trim 元素內(nèi)包含內(nèi)容時(shí),會(huì)把內(nèi)容中匹配的前綴字符串去掉。
suffix :當(dāng) trim 元素內(nèi)包含內(nèi)容時(shí),會(huì)給內(nèi)容增加 suffix 指定的后綴。
suffixOverrides :當(dāng) trim 元素內(nèi)包含內(nèi)容時(shí),會(huì)把內(nèi)容中匹配的后綴字符串去掉。
foreach
SQL 語(yǔ)句中有時(shí)會(huì)使用 IN 關(guān)鍵字,例如 id in ( 1 , 2 , 3 )??梢允褂?{ids}方式直接獲取值,但這種寫法不能防止 SQL 注入,想避免 SQL 注入就需要用#{}的方式,這時(shí)就要配合使用 foreach 標(biāo)簽來(lái)滿足需求。
foreach 可以對(duì)數(shù)組、 Map 或?qū)崿F(xiàn)了工 terable 接口(如 List 、 Set )的對(duì)象進(jìn)行遍歷。數(shù)組在處理時(shí)會(huì)轉(zhuǎn)換為 List 對(duì)象,因此 foreach 遍歷的對(duì)象可以分為兩大類 : Iterable類型和 Map 類型
foreach 實(shí)現(xiàn) in 集合(或數(shù)組)是最簡(jiǎn)單和常用的一種情況
<foreach collection=” lis t ” open=” (” close=” )” separator=” , ” item=” id” index=” i ” >
#{id}
</foreach>
foreach 包含以下屬性。
- collection : 必填,值為要選代循環(huán)的屬性名。這個(gè)屬性值的情況有很多。
- item:變量名,值為從法代對(duì)象中取出的每一個(gè)值。
- index :索引的屬性名,在集合數(shù)組情況下值為當(dāng)前索引值 ,當(dāng)選代循環(huán)的對(duì)象是 Map類型時(shí),這個(gè)值為 Map 的 key (鍵值)。
- open:整個(gè)循環(huán)內(nèi)容開(kāi)頭的字符串 。
- close : 整個(gè)循環(huán)內(nèi)容結(jié)尾的字符串。
- separator :每次循環(huán)的分隔符 。
bind
bind 標(biāo)簽可以使用 OGNL 表達(dá)式創(chuàng)建一個(gè)變量井將其綁定到上下文中。在前面的例子中,UserMapper.xml 有一個(gè) selectByUser 方法,這個(gè)方法用到了 like 查詢條件,部分代碼如下 。
<if test=” userNarne != null and userNarne ! = ””>
and user name like concat ( ’ % ’, #{ userNarne },’ % ’ )
</if>
使用 con cat 函數(shù)連接字符串,在 MySQL 中,這個(gè)函數(shù)支持多個(gè)參數(shù),但在 Oracle 中只支持兩個(gè)參數(shù)。由于不 同數(shù)據(jù)庫(kù)之間的語(yǔ)法差異 ,如果更換數(shù)據(jù)庫(kù),有些 SQL 語(yǔ)句可能就需要重寫。針對(duì)這種情況,可 以使用 bind 標(biāo)簽來(lái)避免由于更換數(shù)據(jù)庫(kù)帶來(lái)的一些麻煩。將上面的
方法改為 bind 方式后,代碼如下。
<if test=” userNarne != null and userNarne !=””>
<bind narne= " userNarneLike ” value = '%'+ userNarne + '%'/>
and user name like #{userNarneLike}
</if>
bind 標(biāo)簽的兩個(gè)屬性都是必選項(xiàng), name 為綁定到上下文的變量名, value 為 OGNL 表
達(dá)式。創(chuàng)建一個(gè) bind 標(biāo)簽的變量后 ,就可以在下面直接使用,使用 bind 拼接字符串不僅可
以避免因更換數(shù)據(jù)庫(kù)而修改 SQL,也能預(yù)防 SQL 注入。