MyBatis中#和$的區(qū)別小結(jié)
MyBatis中#和$的區(qū)別
說一下為什么要寫這篇文章,最近面試有被問到,一下子想不出來有啥區(qū)別,想記錄一下加深自己的理解,同時自己也經(jīng)常用MyBatis-Plus忽略了XML文件的編寫和使用,所以需要加深一下這塊的知識
一、例子
1、#{}將傳入的數(shù)據(jù)當(dāng)作一個字符串,會對傳入的數(shù)據(jù)加上一個雙引號。
比如
select * from student where student_name = #{studentName}如果傳入的值為zhangxiangwei,那么經(jīng)過Mybatis解析完成之后的語句是
select * from student where student_name="zhangxiangwei"
2.${}將傳入的數(shù)據(jù)直接顯示生成在sql中。
比如
select ${fieldName} from user where user_age = 22此時,傳入的參數(shù)作為要查詢的字段,如果傳入的值為user_name,則解析成的sql為:
select user_name from user where user_age = 22
二、區(qū)別
1.#{}方式能夠很大程度上防止sql注入,${}無法防止sql注入。
2.${}方式一般用于傳入數(shù)據(jù)庫對象,例如列表和表名,#{}方式一般用來傳遞接口傳輸過來的具體數(shù)據(jù)。
3.由于#{}方式具有更高的安全行,所以能用#{}的地方盡量不要使用${}。
4.Mybatis排序時使用order by動態(tài)參數(shù)時需要注意,用${}而不是#{}。
5.#符號(Pound Sign):
#符號用于參數(shù)值的占位,會將參數(shù)值進(jìn)行預(yù)編譯,并在執(zhí)行SQL語句時將參數(shù)值安全地傳遞給數(shù)據(jù)庫,防止SQL注入攻擊。適用于大多數(shù)情況,尤其是當(dāng)參數(shù)值來自用戶輸入或不可信數(shù)據(jù)時,是更安全的選擇。
參數(shù)值會被自動轉(zhuǎn)義,不需要擔(dān)心特殊字符的處理。
使用
#符號會產(chǎn)生預(yù)編譯的SQL語句,可以提高數(shù)據(jù)庫的性能,因?yàn)閿?shù)據(jù)庫可以緩存相同的預(yù)編譯語句。
<!-- 使用#符號進(jìn)行參數(shù)占位 -->
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM users
WHERE id = #{userId}
</select>6.$符號(Dollar Sign):
$符號用于簡單的值替換,不進(jìn)行預(yù)編譯,直接將參數(shù)的值嵌入SQL語句中。適用于那些不需要參數(shù)值預(yù)編譯,且參數(shù)值是確定且可信的情況,比如用于動態(tài)拼接SQL語句的情況。
使用
$符號時,需要小心防止SQL注入攻擊,需要手動處理參數(shù)值的安全性。不會產(chǎn)生預(yù)編譯的SQL語句,可能會導(dǎo)致數(shù)據(jù)庫性能較低,因?yàn)槊看蜸QL語句都會重新解析和執(zhí)行。
<!-- 使用$符號進(jìn)行簡單值替換 -->
<select id="getUserByName" parameterType="string" resultType="User">
SELECT * FROM users
WHERE username = '${username}'
</select>三、最后說一下 $ 動態(tài) 拼接SQL
下面是一個實(shí)例
<select id="getUsersWithDynamicSorting" parameterType="map" resultType="User">
SELECT * FROM users
WHERE 1=1 <!-- 為了方便拼接,可以始終使用一個始終成立的條件 -->
<!-- 根據(jù)參數(shù)動態(tài)拼接排序字段和排序順序 -->
<if test="sortField != null and sortOrder != null">
ORDER BY ${sortField} ${sortOrder}
</if>
</select>補(bǔ)充:mybatis中的#{}和${}的區(qū)別
1. 取值范圍不同
MyBatis既可以獲取執(zhí)行SQL時插入的請求參數(shù),也可以從主配置文件加載的配置文件中獲取配置參數(shù)。 #{} 只能獲取請求參數(shù)的值,無法獲取配置參數(shù)。 ${} 在MyBatis初始化時能獲取配置參數(shù),如果沒有,執(zhí)行時再獲取請求參數(shù)。
2. 處理方式不同
#{}
如果SQL中只有 #{} 或者可以被配置參數(shù)替換的 ${} ,那么在初始化時 #{} 就被解析成了占位符 ? 。
如果SQL中有動態(tài)標(biāo)簽(例如 if , where ),或者無法被配置參數(shù)替換的 ${} ,那么 #{} 在執(zhí)行SQL時才會被替換成占位符 ? 。 #{} 的值是執(zhí)行SQL時通過JDBC的Statement根據(jù)占位符進(jìn)行設(shè)置。
${}
如果可以被配置參數(shù)替換,則在初始化時已被配置參數(shù)替換,否則在執(zhí)行SQL時使用請求參數(shù)替換。
${} 的值使用參數(shù)值直接替換,不做任何特殊處理。
3. 安全性不同
#{} 能夠很大程度防止SQL注入。 ${} 無法防止SQL注入。
所以,能用 #{} 的就別用 ${} 。 ${} 方式一般用于傳入數(shù)據(jù)庫對象,例如傳入表名,排序字段等。
4. 測試驗(yàn)證
以下是自己做過的一些測試驗(yàn)證場景:
測試場景一: ${} 是否能夠獲取配置文件中的參數(shù)?如果能獲取配置參數(shù),那么是初始化時替換 ${} 還是執(zhí)行時替換 ${} 。
測試結(jié)果一: ${} 在MyBatis初始化時,獲取MyBatis主配置文件中加載的配置參數(shù)進(jìn)行替換。
- SQL中只有
${},${}被替換成配置參數(shù),在所有${}都被替換的情況下,生成的是靜態(tài)SQL對象。 - SQL中只有
${}和#{},${}被替換成配置參數(shù),在所有${}都被替換的情況下,#{}會被替換成?,且生成靜態(tài)SQL對象。 - SQL中存在
${}和動態(tài)標(biāo)簽,${}無論在動態(tài)標(biāo)簽內(nèi)或者外,${}都被替換成配置參數(shù),生成動態(tài)SQL對象。 - SQL中存在
${}、#{}和動態(tài)標(biāo)簽,${}被替換成配置參數(shù),#{}沒有被替換成?,生成動態(tài)SQL對象。
測試結(jié)論一:
在MyBatis初始化階段,解析XML配置文件的過程中,會對 ${propertyName} 占位符做解析,如果能夠在配置參數(shù)中獲取到 key=propertyName 的值,那么就會使用配置參數(shù)值替換 ${propertyName} ,否則保留。所以在 xxxMapper.xml 中使用 ${propertyName} 獲取參數(shù)時,需要考慮是獲取配置文件中的值,還是獲取SQL執(zhí)行時傳入的請求參數(shù)值。這可能會出現(xiàn)意想不到的結(jié)果。
測試場景二:
如果SQL中有動態(tài)標(biāo)簽或者 ${} 符號,且 ${} 不會被配置參數(shù)替換時,SQL中的 #{} 在初始化時替換成 ? ,還是在執(zhí)行時被替換成 ? 。
測試結(jié)果二:
在MyBatis初始化過程中的測試結(jié)果:
- SQL中僅有
#{},#{}被替換成?。 - SQL中有
#{}和${},#{}沒有被替換成?。SQL中有#{}和動態(tài)標(biāo)簽,#{}無論在動態(tài)標(biāo)簽內(nèi)還是外,都沒有被替換成?。 - SQL中有
#{}和${}、動態(tài)標(biāo)簽,#{}沒有被替換成?。
在SQL執(zhí)行過程中的測試結(jié)果:
- SQL中僅有
#{},#{}在初始化階段就已被替換成了?。 - SQL中有
#{}和${},#{}被替換成?,${}被替換成參數(shù)值。 - SQL中有
#{}和動態(tài)標(biāo)簽,#{}被替換成?,動態(tài)標(biāo)簽根據(jù)參數(shù)被解析。 - SQL中有
#{}、${}和動態(tài)標(biāo)簽,#{}被替換成?,${}被替換成參數(shù)值,動態(tài)標(biāo)簽根據(jù)參數(shù)被解析。
到此這篇關(guān)于MyBatis中#和$的區(qū)別的文章就介紹到這了,更多相關(guān)MyBatis #和$的區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
以Java代碼的方式總結(jié)幾個典型的內(nèi)存溢出案例
作為程序員,多多少少都會遇到一些內(nèi)存溢出的場景,如果你還沒遇到,說明你工作的年限可能比較短,或者你根本就是個假程序員!哈哈,開個玩笑.今天分享給大家Java內(nèi)存溢出的相關(guān)案例,希望大家在日常工作中,盡量避免寫這些low水平的代碼,需要的朋友可以參考下2021-06-06
Springboot實(shí)現(xiàn)根據(jù)用戶ID切換動態(tài)數(shù)據(jù)源
在很多具體應(yīng)用場景中,我們需要用到動態(tài)數(shù)據(jù)源的情況,比如多租戶的場景,系統(tǒng)登錄時需要根據(jù)用戶信息切換到用戶對應(yīng)的數(shù)據(jù)庫。這篇文章主要介紹了SpringBoot根據(jù)用戶ID實(shí)現(xiàn)切換動態(tài)數(shù)據(jù)源的示例代碼,感興趣的可以了解一下2021-12-12
SpringBoot實(shí)現(xiàn)分布式驗(yàn)證碼登錄方案小結(jié)
驗(yàn)證碼登錄作為一種有效的防護(hù)手段,可以防止惡意gongji、暴力pojie等,本文主要介紹了SpringBoot實(shí)現(xiàn)分布式驗(yàn)證碼登錄方案小結(jié),具有一定的參考價值,感興趣的可以了解一下2024-12-12
SpringBoot實(shí)現(xiàn)異步調(diào)用的方法示例
本文介紹了在Java的SpringBoot中實(shí)現(xiàn)異步請求和異步調(diào)用的幾種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01
java向es中寫入數(shù)據(jù)報錯org.elasticsearch.action.ActionReque問題
這篇文章主要介紹了java向es中寫入數(shù)據(jù)報錯org.elasticsearch.action.ActionReque問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11

