關(guān)于MyBatis的foreach標(biāo)簽常用方法
一、前言
在 MyBatis 中,常常會(huì)遇到集合類型的參數(shù),雖然我們可以通過 OGNL 表達(dá)式來(lái)訪問集合的某一個(gè)元素,但是 OGNL 表達(dá)式無(wú)法遍歷集合。foreach 標(biāo)簽就是專門用來(lái)解決這類問題的,foreach 標(biāo)簽可以用來(lái)遍歷數(shù)組、列表和 Map 等集合參數(shù),實(shí)現(xiàn)批量操作或一些簡(jiǎn)單 SQL 操作。
二、foreach 元素屬性簡(jiǎn)介
foreach 元素的屬性主要有 item,index,open,separator,close,collection。各屬性含義如下所示。
2.1 item
集合中元素迭代時(shí)的別名,該參數(shù)為必選。
2.2 index
在 list 和數(shù)組中,index 是元素的序號(hào);在 map 中,index 是元素的 key。該參數(shù)可選。
2.3 open
foreach 代碼的開始符號(hào),一般是 ”(“,和 close=“)” 合用。常用在 in(),values() 時(shí)。該參數(shù)可選。
2.4 separator
元素之間的分隔符,例如在 in() 的時(shí)候,separator=“,” 會(huì)自動(dòng)在元素中間用 “,“ 隔開,避免手動(dòng)輸入逗號(hào)導(dǎo)致 SQL 錯(cuò)誤,如 in(1, 2,) 這樣。該參數(shù)可選。
2.5 close
foreach 代碼的關(guān)閉符號(hào),一般是 ”)“,和 open=“(” 合用。常用在 in(),values()時(shí)。該參數(shù)可選。
2.6 collection
要被 foreach 標(biāo)簽循環(huán)解析的對(duì)象。
foreach 標(biāo)簽的 collection 屬性在接受參數(shù)名時(shí),有兩種情況:
- 匿名參數(shù)
當(dāng)在 java 方法中沒有通過 @Param 注解指定參數(shù)名時(shí),列表類型默認(rèn)參數(shù)名為 ”list“,數(shù)組類型默認(rèn)參數(shù)名為 ”array“,Map 對(duì)象沒有默認(rèn)值。 - 具名參數(shù)
java 方法中使用了 @Param 注解指定了參數(shù)名稱,則 foreach 中的 collection 屬性必須為參數(shù)名。
在作為入?yún)r(shí)可以使用 @Param(“keyName”) 來(lái)設(shè)置該鍵值,設(shè)置 keyName 后,list、array 將會(huì)失效。除了入?yún)⑦@種情況外,還有一種是作為參數(shù)對(duì)象的某個(gè)字段,例子如下。如果 User 有屬性 List ids。入?yún)⑹?User 對(duì)象,那么這個(gè)collection = “ids”。如果 User 有屬性 Ids ids,其中 Ids 是個(gè)對(duì)象,Ids 有個(gè)屬性 List id,入?yún)⑹?User 對(duì)象,那么 collection = “ids.id”。
注意點(diǎn):在使用 foreach 的時(shí)候,最關(guān)鍵的就是 collection 屬性,該屬性是必須指定的,但是在不同情況下,該屬性的值是不一樣的,主要有以下 3 種情況(未通過 @Param 指定別名時(shí))。
- 如果傳入的是單參數(shù)且參數(shù)類型是一個(gè) List 的時(shí)候,collection 屬性值為 ”list“。
- 如果傳入的是單參數(shù)且參數(shù)類型是一個(gè) array 數(shù)組的時(shí)候,collection 的屬性值為 ”array“。
- 如果傳入的參數(shù)是多個(gè)的時(shí)候,我們可以把它們封裝成一個(gè) Map,當(dāng)然單參數(shù)也可以封裝成 map。實(shí)際上如果你在傳入?yún)?shù)的時(shí)候,在 MyBatis 里面也是會(huì)把它封裝成一個(gè) Map 的,map 的 key 就是參數(shù)名,所以這個(gè)時(shí)候 collection 屬性值就是傳入的 List 或 array 對(duì)象在自己封裝的 map 里面的 key。
三、#{} 與 ${} 的區(qū)別
在使用參數(shù)的過程中,會(huì)遇到 #{} 與 ${} 的問題,因此簡(jiǎn)單總結(jié)下兩者之間的區(qū)別。
- ${param} 傳遞的參數(shù)會(huì)被當(dāng)成 SQL 語(yǔ)句中的一部分,比如傳遞表名,字段名,字段類型等數(shù)據(jù)。 例如,傳入值為 id,order by ${param} 則解析成 SQL:order by id。
- #{parm} 傳入的數(shù)據(jù)都當(dāng)成一個(gè)字符串,會(huì)對(duì)自動(dòng)傳入的數(shù)據(jù)加一個(gè)雙引號(hào)。 例如,傳入值為 id,select * from table where name = #{param} 則解析成 SQL:select * from table where name = ? (其中問號(hào)在執(zhí)行時(shí)傳入值 “id”)。
為了安全,能用 # 的地方就用 # 方式傳參,這樣可以有效的防止 SQL 注入攻擊。
官方說(shuō)明:mybatis 在處理 #{} 時(shí),會(huì)將 SQL 中的 #{} 替換為 “?”,調(diào)用 PreparedStatement 的 set 方法來(lái)賦值;mybatis 在處理 ${} 時(shí),就是把 ${} 替換成變量的值。使用 #{} 可以有效的防止 SQL 注入,提高系統(tǒng)安全性。
四、實(shí)例實(shí)戰(zhàn)
本文以如下幾個(gè)例子簡(jiǎn)單總結(jié)下 foreach 是如何遍歷列表、數(shù)組和 Map 的。
4.1 遍歷 List<String> 列表
Java 層接口
List<Rule> selectRulesByList(List<String> ids);
XML
<select id="selectRulesByList" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List" />
FROM tbl_test_rule WHERE rule_id IN
<foreach collection="list" open="(" close=")" separator="," item="item" index="index">
#{item}
</foreach>
</select>運(yùn)行時(shí) SQL 語(yǔ)句
==> Preparing: SELECT rule_id, rule_name, rule_type FROM tbl_test_rule WHERE rule_id IN ( ? , ? ) ==> Parameters: 10001(String), 20002(String) <== Total: 2
4.2 遍歷 List<Object> 列表
項(xiàng)目中定義了一個(gè)實(shí)體類 Rule,在批量插入時(shí)需要遍歷 List<Rule>,實(shí)現(xiàn)方式見下文。
Java 層接口
int insertRules(@Param("rules") List<Rule> rules);XML
<insert id="insertRules">
INSERT INTO tbl_test_rule (rule_id, rule_name, rule_type) VALUES
<foreach collection="rules" separator="," item="rule">
(#{rule.ruleId}, #{rule.ruleName}, #{rule.ruleType})
</foreach>
</insert>運(yùn)行時(shí) SQL 語(yǔ)句
==> Preparing: INSERT INTO tbl_test_rule (rule_id, rule_name, rule_type) VALUES (?, ?, ?) , (?, ?, ?) ==> Parameters: ruleId1(String), ruleName1(String), 1(String), ruleId2(String), ruleName2(String), 2(String) <== Updates: 2
4.3 遍歷數(shù)組
Java 層接口
List<Rule> selectRulesByArray(String[] ids);
如果 ids 參數(shù)使用 @Param 注解指定了參數(shù)名稱,則 foreach 標(biāo)簽中的 collection 屬性必須為該名稱;但若未指定名稱,則在 foreach 標(biāo)簽中使用默認(rèn)數(shù)組名稱 array,如下所示
XML
<select id="selectRulesByArray" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List" />
FROM tbl_test_rule WHERE rule_id IN
<foreach collection="array" open="(" close=")" separator="," item="item" index="index">
#{item}
</foreach>
</select>運(yùn)行時(shí) SQL 語(yǔ)句
==> Preparing: SELECT rule_id, rule_name, rule_type FROM tbl_test_rule WHERE rule_id IN ( ? , ? ) ==> Parameters: 10001(String), 20002(String) <== Total: 2
4.4 遍歷 Map 實(shí)現(xiàn) insert into … on duplicate key update
數(shù)據(jù)庫(kù)表 tbl_test_discount,聯(lián)合主鍵(cert_no, rule_id, cycle_id),存儲(chǔ)了不同用戶不同周期下的折扣金額。請(qǐng)求參數(shù) Map 中存儲(chǔ)了某一用戶不同規(guī)則(key 為不同的 rule_id 值)和各個(gè)規(guī)則下的折扣值(value 為 dis_sum),如下方 Java 接口定義。需要實(shí)現(xiàn):當(dāng)數(shù)據(jù)庫(kù)中無(wú)主鍵記錄時(shí),將記錄插入數(shù)據(jù)庫(kù);如數(shù)據(jù)庫(kù)中存在主鍵記錄時(shí),更新折扣值,將折扣值累加計(jì)算(即實(shí)現(xiàn) insert into … on duplicate key update 操作)。過程示例如下。
Java 層接口
int saveOrUpd(@Param(value = "certNo") String certNo,
@Param(value = "cycleId") String cycleId,
@Param(value = "params") Map map);使用 foreach 標(biāo)簽遍歷 Map 時(shí),collection 屬性值為 @param 注解指定的參數(shù)名,即 params,且 item 是 Map 的鍵值,index 是鍵名。
XML
<update id="saveOrUpd" parameterType="java.util.Map">
<foreach collection="params" index="key" item="value">
insert into tbl_test_discount (cert_no, rule_id, cycle_id, dis_sum) values
(#{certNo}, #{key}, #{cycleId}, #{value}) on duplicate key update dis_sum = dis_sum + #{value};
</foreach>
</update>mybatis 設(shè)置允許批量更新
mybatis 會(huì)根據(jù)上述 XML 文件的配置,動(dòng)態(tài)生成多條 SQL。要讓 mybatis 成功執(zhí)行多條語(yǔ)句,須開啟允許批量查詢?cè)O(shè)置,即在 jdbc-url 連接信息中添加 &allowMultiQueries=true,如下所示。
spring.datasource.jdbc-url=jdbc:mysql://localhost:3306/testdb?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true
MySQL 連接數(shù)據(jù)庫(kù)時(shí),添加語(yǔ)句 &allowMultiQueries=true 的作用:可以在 SQL 語(yǔ)句后攜帶分號(hào),實(shí)現(xiàn)多語(yǔ)句執(zhí)行;可以執(zhí)行批處理,同時(shí)發(fā)出多個(gè) SQL 語(yǔ)句。
運(yùn)行時(shí) SQL 語(yǔ)句
==> Preparing: insert into tbl_test_discount (cert_no, rule_id, cycle_id, dis_sum) values (?, ?, ?, ?) on duplicate key update dis_sum = dis_sum + ?; insert into tbl_test_discount (cert_no, rule_id, cycle_id, dis_sum) values (?, ?, ?, ?) on duplicate key update dis_sum = dis_sum + ?; insert into tbl_test_discount (cert_no, rule_id, cycle_id, dis_sum) values (?, ?, ?, ?) on duplicate key update dis_sum = dis_sum + ?; insert into tbl_test_discount (cert_no, rule_id, cycle_id, dis_sum) values (?, ?, ?, ?) on duplicate key update dis_sum = dis_sum + ?; ==> Parameters: testCertNo1(String), 30001(String), 202212(String), 225(Integer), 225(Integer), testCertNo2(String), 20002(String), 202212(String), 385(Integer), 385(Integer), testCertNo3(String), 20001(String), 202212(String), 553(Integer), 553(Integer), testCertNo4(String), 10001(String), 202212(String), 300(Integer), 300(Integer) <== Updates: 1
foreach 標(biāo)簽是使用非常廣泛的一個(gè)標(biāo)簽,當(dāng)使用 SQL 進(jìn)行批量插入、查詢時(shí)都可能使用到它。列表遍歷的使用最為廣泛,數(shù)組和 Map 則相對(duì)較少。
到此這篇關(guān)于關(guān)于MyBatis的foreach標(biāo)簽常用方法的文章就介紹到這了,更多相關(guān)MyBatis的foreach標(biāo)簽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis使用<foreach>標(biāo)簽like查詢報(bào)錯(cuò)解決問題
- MyBatis使用<foreach>標(biāo)簽報(bào)錯(cuò)問題及解決
- Mybatis使用foreach標(biāo)簽實(shí)現(xiàn)批量插入方式
- MyBatis之foreach標(biāo)簽的用法及多種循環(huán)問題
- MyBatis中foreach標(biāo)簽的collection屬性的取值方式
- mybatis中foreach嵌套if標(biāo)簽方式
- mybatis?<foreach>標(biāo)簽動(dòng)態(tài)增刪改查方式
- Mybatis之foreach標(biāo)簽內(nèi)傳入list為空的問題
相關(guān)文章
myeclipse安裝Spring Tool Suite(STS)插件的方法步驟
這篇文章主要介紹了myeclipse安裝Spring Tool Suite(STS)插件的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
解決springboot?druid數(shù)據(jù)庫(kù)連接池連接失敗后一直重連問題
這篇文章主要介紹了解決springboot?druid數(shù)據(jù)庫(kù)連接池連接失敗后一直重連問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
Spring?Cache抽象-使用SpEL表達(dá)式解析
這篇文章主要介紹了Spring?Cache抽象-使用SpEL表達(dá)式解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
ShardingSphere結(jié)合MySQL實(shí)現(xiàn)分庫(kù)分表的項(xiàng)目實(shí)踐
在實(shí)際開發(fā)中,如果表的數(shù)據(jù)過大我們需要把一張表拆分成多張表,本文主要介紹了使用ShardingSphere實(shí)現(xiàn)MySQL分庫(kù)分表,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
基于java集合中的一些易混淆的知識(shí)點(diǎn)(詳解)
下面小編就為大家?guī)?lái)一篇基于java集合中的一些易混淆的知識(shí)點(diǎn)(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2016-09-09
@RequestBody,@RequestParam和@Param的區(qū)別說(shuō)明
這篇文章主要介紹了@RequestBody,@RequestParam和@Param的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

