Mybatis的Mapper映射文件中,有兩種方式可以引用形參變量進(jìn)行取值: #{} 和 ${}
本文將簡(jiǎn)述兩種方式的區(qū)別和適用場(chǎng)景

取值引用
#{} 方式
#{}: 解析為SQL時(shí),會(huì)將形參變量的值取出,并自動(dòng)給其添加引號(hào)。
例如:當(dāng)實(shí)參username="Amy"時(shí),傳入下Mapper映射文件后
......
<select id="findByName" parameterType="String" resultMap="studentResultMap">
SELECT * FROM user WHERE username=#{value}
</select>
....
SQL將解析為:
SELECT * FROM user WHERE username="Amy"
${} 方式
${}: 解析為SQL時(shí),將形參變量的值直接取出,直接拼接顯示在SQL中
例如:當(dāng)實(shí)參username="Amy"時(shí),傳入下Mapper映射文件后
......
<select id="findByName" parameterType="String" resultMap="studentResultMap">
SELECT * FROM user WHERE username=${value}
</select>
....
SQL將解析如下:
SELECT * FROM user WHERE username=Amy
顯而該SQL無(wú)法正常執(zhí)行,故需要在mppaer映射文件中的${value}前后手動(dòng)添加引號(hào),如下所示:
......
<select id="findByName" parameterType="String" resultMap="studentResultMap">
SELECT * FROM user WHERE username='${value}'
</select>
....
SQL將解析為:
SELECT * FROM user WHERE username='Amy'
SQL 注入
${}方式是將形參和SQL語(yǔ)句直接拼接形成完整的SQL命令后,再進(jìn)行編譯,所以可以通過(guò)精心設(shè)計(jì)的形參變量的值,來(lái)改變?cè)璖QL語(yǔ)句的使用意圖從而產(chǎn)生安全隱患,即為SQL注入攻擊?,F(xiàn)舉例說(shuō)明:
現(xiàn)有Mapper映射文件如下:
......
<select id="findByName" parameterType="String" resultMap="studentResultMap">
SELECT * FROM user WHERE username='${value}'
</select>
....
當(dāng) username = "' OR 1=1 OR '" 傳入后,${}將變量?jī)?nèi)容直接和SQL語(yǔ)句進(jìn)行拼接,結(jié)果如下:
SELECT * FROM user WHERE username='' OR 1=1 OR '';
顯而易見(jiàn),上述語(yǔ)句將把整個(gè)數(shù)據(jù)庫(kù)內(nèi)容直接暴露出來(lái)了
#{}方式則是先用占位符代替參數(shù)將SQL語(yǔ)句先進(jìn)行預(yù)編譯,然后再將參數(shù)中的內(nèi)容替換進(jìn)來(lái)。由于SQL語(yǔ)句已經(jīng)被預(yù)編譯過(guò),其SQL意圖將無(wú)法通過(guò)非法的參數(shù)內(nèi)容實(shí)現(xiàn)更改,其參數(shù)中的內(nèi)容,無(wú)法變?yōu)镾QL命令的一部分。故,#{}可以防止SQL注入而${}卻不行
適用場(chǎng)景
#{} 和 ${} 均適用場(chǎng)景
由于SQL注入的原因,${}和#{}在都可以使用的場(chǎng)景下,很明顯推薦使用#{}。這里除了上文的WHERE語(yǔ)句例子,再介紹一個(gè)LIKE模糊查詢的場(chǎng)景(username = "Amy"):
<select id="findAddByName" parameterType="String" resultMap="studentResultMap">
SELECT * FROM user WHERE username LIKE '%${value}%'
</select>
該SQL解析為:
SELECT * FROM user WHERE username LIKE '%Amy%';
上述通過(guò)${}雖然可以實(shí)現(xiàn)對(duì)包含"Amy"對(duì)模糊查詢,但是不安全,可以改用#{},如下所示:
<select id="findAddByName" parameterType="String" resultMap="studentResultMap">
SELECT * FROM USER WHERE username LIKE CONCAT('%', #{username}, '%')
</select>
該SQL解析為下文所示,其效果和上文方式一致
SELECT * FROM USER WHERE username LIKE CONCAT('%', 'Amy','%');
只能使用${}的場(chǎng)景
由于#{}會(huì)給參數(shù)內(nèi)容自動(dòng)加上引號(hào),會(huì)在有些需要表示字段名、表名的場(chǎng)景下,SQL將無(wú)法正常執(zhí)行?,F(xiàn)舉一例說(shuō)明:
期望查詢結(jié)果按sex字段升序排列,參數(shù)String orderCol = "sex",mapper映射文件使用#{}:
<select id="findAddByName3" parameterType="String" resultMap="studentResultMap">
SELECT * FROM USER WHERE username LIKE '%Am%' ORDER BY #{value} ASC
</select>
則SQL解析及執(zhí)行結(jié)果如下所示,很明顯 ORDER 子句的字段名錯(cuò)誤的被加上了引號(hào),致使查詢結(jié)果沒(méi)有按期排序輸出
SELECT * FROM USER WHERE username LIKE '%Am%' ORDER BY 'sex' ASC;

這時(shí),現(xiàn)改為${}測(cè)試效果:
<select id="findAddByName3" parameterType="String" resultMap="studentResultMap">
SELECT * FROM USER WHERE username LIKE '%Am%' ORDER BY ${value} ASC
</select>
則SQL解析及執(zhí)行結(jié)果如下所示:
SELECT * FROM USER WHERE username LIKE '%Am%' ORDER BY sex ASC;
