#{}: 表示一個(gè)占位符號(hào),實(shí)現(xiàn)向PreparedStatement占位符中設(shè)置值(#{}表示一個(gè)占位符?),自動(dòng)進(jìn)行Java類型到JDBC類型的轉(zhuǎn)換(因此#{}可以有效防止SQL注入).#{}可以接收簡(jiǎn)單類型或PO屬性值,如果parameterType傳輸?shù)氖菃蝹€(gè)簡(jiǎn)單類型值,#{}花括號(hào)中可以是value或其它名稱.
${}: 表示拼接SQL串,通過${}可將parameterType內(nèi)容拼接在SQL中而不進(jìn)行JDBC類型轉(zhuǎn)換,${}可以接收簡(jiǎn)單類型或PO屬性值,如果parameterType傳輸?shù)氖菃蝹€(gè)簡(jiǎn)單類型值,${}花括號(hào)中只能是value.
雖然${}不能防止SQL注入,但有時(shí)${}會(huì)非常方便(如order by排序,需要將列名通過參數(shù)傳入SQL,則用ORDER BY ${column},使用#{}則無法實(shí)現(xiàn)此功能.
動(dòng)態(tài) sql 是 mybatis 的主要特性之一祥诽,在 mapper 中定義的參數(shù)傳到 xml 中之后保礼,在查詢之前 mybatis 會(huì)對(duì)其進(jìn)行動(dòng)態(tài)解析诵竭。mybatis 為我們提供了兩種支持動(dòng)態(tài) sql 的語(yǔ)法:#{} 以及 ${}醒叁。
在下面的語(yǔ)句中回窘,如果 username 的值為 zhangsan冯挎,則兩種方式無任何區(qū)別:
select*fromuserwherename= #{name};
select*fromuserwherename=${name};
其解析之后的結(jié)果均為
select*fromuserwherename='zhangsan';
但是?#{} 和 ${} 在預(yù)編譯中的處理是不一樣的泉瞻。#{} 在預(yù)處理時(shí)系吭,會(huì)把參數(shù)部分用一個(gè)占位符 ? 代替五嫂,變成如下的 sql 語(yǔ)句:
select*fromuserwherename=?;
而 ${} 則只是簡(jiǎn)單的字符串替換,在動(dòng)態(tài)解析階段肯尺,該 sql 語(yǔ)句會(huì)被解析成
select*fromuserwherename='zhangsan';
以上沃缘,#{} 的參數(shù)替換是發(fā)生在 DBMS 中,而 ${} 則發(fā)生在動(dòng)態(tài)解析過程中则吟。
那么槐臀,在使用過程中我們應(yīng)該使用哪種方式呢?
答案是氓仲,優(yōu)先使用 #{}水慨。因?yàn)?${} 會(huì)導(dǎo)致 sql 注入的問題【纯福看下面的例子:
select*from${tableName}wherename=#{name}
在這個(gè)例子中讥巡,如果表名為
?user; delete user; --?
則動(dòng)態(tài)解析之后 sql 如下:
select*fromuser;deleteuser;-- where name = ?;
--之后的語(yǔ)句被注釋掉,而原本查詢用戶的語(yǔ)句變成了查詢所有用戶信息+刪除用戶表的語(yǔ)句舔哪,會(huì)對(duì)數(shù)據(jù)庫(kù)造成重大損傷欢顷,極大可能導(dǎo)致服務(wù)器宕機(jī)。
但是表名用參數(shù)傳遞進(jìn)來的時(shí)候捉蚤,只能使用 ${}?