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