#{}和${}這兩個(gè)語法是為了動(dòng)態(tài)傳遞參數(shù)而存在的竞惋,是Mybatis實(shí)現(xiàn)動(dòng)態(tài)SQL的基礎(chǔ)柜去,總體上他們的作用是一致的(為了動(dòng)態(tài)傳參),但是在編譯過程拆宛、是否自動(dòng)加單引號嗓奢、安全性、使用場景等方面有很多不同浑厚,下面詳細(xì)比較兩者間的區(qū)別蔓罚。
預(yù)編譯可以類比java類的編譯椿肩,java類被編譯成class文件,載入虛擬機(jī)豺谈,載入虛擬機(jī)的字節(jié)碼文件可以先被編譯成機(jī)器碼郑象,那么在執(zhí)行某行代碼的時(shí)候就可以直接執(zhí)行編譯后的機(jī)器碼,而不用從字節(jié)碼開始編譯再執(zhí)行茬末,那么執(zhí)行效率就高了厂榛。這也是為啥熱機(jī)狀態(tài)比冷機(jī)狀態(tài)可以抗更多負(fù)載的原因。
Sql的預(yù)編譯也是一樣的道理丽惭,在執(zhí)行前就編譯好击奶,等執(zhí)行時(shí)直接取編譯結(jié)果去執(zhí)行。省去編譯時(shí)間责掏。Sql預(yù)編譯后會在參數(shù)位置用占位符表示柜砾。所以預(yù)編譯就是:數(shù)據(jù)庫驅(qū)動(dòng)在發(fā)送Sql和參數(shù)到DBMS之前,先對Sql語句進(jìn)行編譯處理换衬,之后DBMS則可以直接對Sql進(jìn)行處理痰驱,不需要再次編譯,提高了性能瞳浦。這一點(diǎn)mybatis 默認(rèn)情況下担映,將對所有的Sql 進(jìn)行預(yù)編譯處理。
預(yù)編譯可以將多個(gè)操作步驟合并成一個(gè)步驟叫潦,一般而言蝇完,越復(fù)雜的sql,編譯程度也會復(fù)雜矗蕊,難度大短蜕,耗時(shí),費(fèi)性能傻咖,而預(yù)編譯可以合并這些操作忿危,預(yù)編譯之后DBMS可以省去編譯直接運(yùn)行sql。
預(yù)編譯語句可以重復(fù)利用没龙。把一個(gè) sql 預(yù)編譯后產(chǎn)生的 PreparedStatement 對象緩存下來,下次對于同一個(gè)sql缎玫,可以直接使用這個(gè)緩存的 PreparedState 對象硬纤。
上面列舉了#{}?和${}?的區(qū)別,接下來結(jié)合代碼及SQL語句打印日志進(jìn)行說明:
關(guān)于SQL語句打印赃磨,若是Spring Boot集成的Mybatis項(xiàng)目可以在application.yml文件中加入以下配置:
#{}動(dòng)態(tài)獲取id時(shí)筝家,sql語句的打印顯示的是一個(gè)?“1”?,沒有占位符邻辉,直接顯示溪王。
主要指的就是防SQL注入腮鞍,什么是SQL注入?以上面的角色查詢?yōu)槔喝绻藭r(shí)的傳參 name = "富貴 or name = 狗蛋"?
可以看到SQL語句的查詢規(guī)則已經(jīng)改變莹菱,原本是查一個(gè)叫名字叫“富貴 or name=狗蛋”的角色 移国,現(xiàn)在變成了查一個(gè)名字叫“富貴”或者叫“狗蛋”的角色,這種通過傳參就能改變SQL語句原本規(guī)則的操作就是SQL注入道伟,這個(gè)在實(shí)際生產(chǎn)中當(dāng)然是危險(xiǎn)的迹缀,攻擊者可以把SQL命令插入到Web表單的輸入域或頁面請求的查詢字符串中,欺騙服務(wù)器執(zhí)行惡意的SQL命令蜜徽。
可以看到祝懂,${} 直接拼接的方式可能引起SQL注入,不安全拘鞋。那 #{} 為啥就可以防止SQL注入呢砚蓬?
這里用Mybatis默認(rèn)的?0?和 1 來代替?zhèn)鲄ⅲ鶕?jù)版本不同盆色,也可能是用arg0灰蛙、arg1、arg2傅事,或者 param1 缕允、param2 ,可以根據(jù)報(bào)錯(cuò)信息進(jìn)行修改蹭越,如下:
${}?時(shí)障本,用 0 和 1雖然不會報(bào)錯(cuò),但是會直接當(dāng)成參數(shù)執(zhí)行响鹃。一般默認(rèn)參數(shù)是param1驾霜、param2...
1、能用?#{}?的地方就用?#{}买置,盡量少用?${}
2粪糙、表名作參數(shù),或者order by 排序時(shí)用?${}
3忿项、傳參時(shí)參數(shù)使用@Param("")注解
tips:
@Param注解的作用是給參數(shù)命名蓉冈,參數(shù)命名后就能根據(jù)名字得到參數(shù)值(相當(dāng)于又加了一層密)如:
Role selectById(@Param("roleId")?String id);