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

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

這時(shí),現(xiàn)改為${}測試效果:
<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;
