Mybatis的mapper文件中#和$的區(qū)別示例解析
MyBatis 中 mapper 文件里 #{} 與 ${} 的核心區(qū)別
MyBatis 的 mapper 文件中,#{}和${}是兩種參數(shù)占位符,核心差異在于參數(shù)解析方式、SQL 注入風(fēng)險(xiǎn)、適用場景,以下從底層原理、使用場景、示例等維度詳細(xì)解析:
一、核心區(qū)別對比表
| 特性 | #{} | ${} |
|---|---|---|
| 解析方式 | 預(yù)編譯處理:MyBatis 將#{}替換為?,通過 PreparedStatement 設(shè)置參數(shù)(setXxx()) | 字符串替換:直接將參數(shù)拼接到 SQL 語句中,無預(yù)編譯 |
| SQL 注入風(fēng)險(xiǎn) | 安全(參數(shù)會(huì)被自動(dòng)轉(zhuǎn)義) | 危險(xiǎn)(直接拼接,易觸發(fā) SQL 注入) |
| 參數(shù)類型處理 | 自動(dòng)識別參數(shù)類型,自動(dòng)加引號(如字符串參數(shù)拼接后為'value') | 不處理類型,直接拼接(字符串需手動(dòng)加引號) |
| 適用場景 | 常規(guī)參數(shù)傳遞(列值、條件值) | 動(dòng)態(tài)表名、列名、排序字段等 SQL 語法部分 |
| 性能 | 預(yù)編譯可復(fù)用執(zhí)行計(jì)劃,性能高 | 每次拼接生成新 SQL,性能低 |
二、底層原理與示例
1.#{}:預(yù)編譯占位符(推薦)
原理
MyBatis 解析#{}時(shí),會(huì)將其替換為 JDBC 的?占位符,生成PreparedStatement,并通過setInt()/setString()等方法設(shè)置參數(shù),參數(shù)會(huì)被自動(dòng)轉(zhuǎn)義(如特殊字符'會(huì)被處理為\'),從根本上避免 SQL 注入。
示例
Mapper 文件:
<select id="getUserById" resultType="User">
SELECT id, name, age FROM user WHERE id = #{id}
</select>實(shí)際執(zhí)行的 SQL(參數(shù) id=1):
-- MyBatis生成PreparedStatement,參數(shù)通過setInt(1, 1)設(shè)置 SELECT id, name, age FROM user WHERE id = ?
字符串參數(shù)示例(name="張三"):
<select id="getUserByName" resultType="User">
SELECT id, name, age FROM user WHERE name = #{name}
</select>實(shí)際執(zhí)行:參數(shù)會(huì)被自動(dòng)加引號,等價(jià)于WHERE name = '張三',若參數(shù)含特殊字符(如張三' OR 1=1),會(huì)被轉(zhuǎn)義為'張三\' OR 1=1',避免注入。
2.${}:字符串拼接(慎用)
原理
MyBatis 直接將${}替換為參數(shù)的原始字符串,無任何轉(zhuǎn)義處理,相當(dāng)于 “硬拼接” SQL,因此存在嚴(yán)重的 SQL 注入風(fēng)險(xiǎn),但可用于動(dòng)態(tài)指定 SQL 語法的部分(如表名、列名)。
示例
動(dòng)態(tài)表名(按年月分表):
<select id="getUserByMonth" resultType="User">
SELECT id, name FROM user_${month} WHERE id = #{id}
</select>參數(shù) month="202512",id=1 時(shí),實(shí)際執(zhí)行 SQL:
SELECT id, name FROM user_202512 WHERE id = ?
風(fēng)險(xiǎn)示例(SQL 注入):若使用${}接收用戶輸入的排序字段:
<select id="getUserList" resultType="User">
SELECT id, name FROM user ORDER BY ${sortField}
</select>當(dāng)用戶傳入sortField="id; DROP TABLE user;"時(shí),拼接后的 SQL 為:
SELECT id, name FROM user ORDER BY id; DROP TABLE user;
直接導(dǎo)致表被刪除,風(fēng)險(xiǎn)極高。
三、關(guān)鍵使用原則
1. 優(yōu)先使用#{}
所有常規(guī)參數(shù)(如查詢條件、插入 / 更新的列值)必須用#{},杜絕 SQL 注入風(fēng)險(xiǎn),同時(shí)享受預(yù)編譯的性能優(yōu)勢。
2. 僅在必要時(shí)使用${}
僅當(dāng)需要?jiǎng)討B(tài)指定 SQL 語法元素時(shí)使用${},且必須做嚴(yán)格的參數(shù)校驗(yàn):
// 對${}參數(shù)做白名單校驗(yàn)(示例:僅允許指定的排序字段)
public List<User> getUserList(String sortField) {
// 白名單
List<String> allowedFields = Arrays.asList("id", "name", "age");
if (!allowedFields.contains(sortField)) {
sortField = "id"; // 默認(rèn)值,防止注入
}
return userMapper.getUserList(sortField);
}3. 特殊場景的兼容處理
模糊查詢:#{}可配合CONCAT使用(推薦),而非${}:
<!-- 正確:避免注入 -->
SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')
<!-- 錯(cuò)誤:易注入 -->
SELECT * FROM user WHERE name LIKE '%${name}%'批量插入:MyBatis 的foreach標(biāo)簽中,集合元素用#{}:
<insert id="batchInsert">
INSERT INTO user (name, age) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.name}, #{item.age})
</foreach>
</insert>四、總結(jié)
| 場景 | 推薦用法 | 注意事項(xiàng) |
|---|---|---|
| 傳遞列值 / 條件值 | #{} | 自動(dòng)轉(zhuǎn)義,無注入風(fēng)險(xiǎn) |
| 動(dòng)態(tài)表名 / 列名 / 排序 | ${} + 白名單 | 必須校驗(yàn)參數(shù),限制取值范圍 |
| 模糊查詢 | #{} + CONCAT | 避免直接拼接%${}% |
核心記?。?code>#{}是 “安全的預(yù)編譯”,${}是 “危險(xiǎn)的字符串拼接”,開發(fā)中除非明確需要?jiǎng)討B(tài)拼接 SQL 語法,否則一律使用#{}。
到此這篇關(guān)于Mybatis的mapper文件中#和$的區(qū)別的文章就介紹到這了,更多相關(guān)mybatis #和$區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring mvc中的@PathVariable獲得請求url中的動(dòng)態(tài)參數(shù)
本文主要介紹了spring mvc中的@PathVariable獲得請求url中的動(dòng)態(tài)參數(shù)的代碼。具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-02-02
spring cloud openfeign 源碼實(shí)例解析
這篇文章主要介紹了spring cloud openfeign 源碼實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Java 帶參數(shù)與帶返回值的方法的定義和調(diào)用
在java中,方法就是用來完成解決某件事情或?qū)崿F(xiàn)某個(gè)功能的辦法。方法實(shí)現(xiàn)的過程中,會(huì)包含很多條語句用于完成某些有意義的功能——通常是處理文本,控制輸入或計(jì)算數(shù)值,這篇文章我們來探究一下帶參數(shù)與帶返回值的方法的定義和調(diào)用2022-04-04
SpringBoot中EasyExcel實(shí)現(xiàn)execl導(dǎo)入導(dǎo)出
本文主要介紹了SpringBoot中EasyExcel實(shí)現(xiàn)execl導(dǎo)入導(dǎo)出,實(shí)現(xiàn)了如何準(zhǔn)備環(huán)境、創(chuàng)建實(shí)體類、自定義轉(zhuǎn)換器以及編寫導(dǎo)入邏輯的步驟和示例代碼,感興趣的可以了解下2023-06-06
java導(dǎo)出Excel通用方法的實(shí)例詳解
這篇文章主要介紹了java導(dǎo)出Excel通用方法的實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07
SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法
這篇文章主要介紹了SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02

