動(dòng)態(tài)sql是mybatis的主要特性之一。在mapper中定義的參數(shù)傳到xml中之后贼急,在查詢(xún)之前mybatis會(huì)對(duì)其進(jìn)行動(dòng)態(tài)解析茅茂。
mybatis提供了兩種支持動(dòng)態(tài)sql的語(yǔ)法:#{} 、${}太抓。
select * from t_user where username = '${username}';
select * from t_user where username = #{username};
username傳參一致的話,這兩種執(zhí)行的結(jié)果是一樣的空闲,但是這兩種方式在動(dòng)態(tài)sql解析階段的處理是不一樣的。
1腻异、#{}
解析為一個(gè)JDBC預(yù)編譯語(yǔ)句(prepared statement)的參數(shù)標(biāo)記符,把參數(shù)部分用占位符这揣?代替悔常。動(dòng)態(tài)解析為:
select * from t_user where username = ? ;
而傳入的參數(shù)將會(huì)經(jīng)過(guò)PreparedStatement方法的強(qiáng)制類(lèi)型檢查和安全檢查等處理给赞,最后作為一個(gè)合法的字符串傳入机打。
2、${}
這種方式只會(huì)做簡(jiǎn)單的字符串替換片迅,在動(dòng)態(tài)SQL解析階段將會(huì)進(jìn)行變量替換残邀,假如傳遞的參數(shù)為Alice,最終處理結(jié)果如下:
select * from t_user where username = 'Alice' ;
這樣在預(yù)編譯之前的sql語(yǔ)句已經(jīng)不包含變量了柑蛇,因此可以看出${} 變量的替換階段是在動(dòng)態(tài)SQL解析階段芥挣。
3、#{} ${}兩種方式對(duì)比
1)是否預(yù)防SQL注入
以上不同的處理方式可以看出耻台,#{}預(yù)處理之后可以預(yù)防SQL注入空免;而${}在預(yù)編譯之前就已經(jīng)被替換,有被注入的風(fēng)險(xiǎn)盆耽,如下例:
如果傳入的username 為 a' or '1=1蹋砚,那么使用${}處理后直接替換字符串的sql就解析為:
select * from t_user where username = a' or '1=1' ;
這樣的話所有的用戶數(shù)據(jù)就被查出來(lái)了扼菠,這樣就屬于SQL注入。
如果使用#{}坝咐,經(jīng)過(guò)sql動(dòng)態(tài)解析和預(yù)編譯循榆,會(huì)把單引號(hào)轉(zhuǎn)義為\'那么sql最終解析為:
select * from t_user where username = "a\' or \'1=1 ";//這樣會(huì)查不出任何數(shù)據(jù),有效阻止sql注入
有的業(yè)務(wù)場(chǎng)景經(jīng)常用到模糊查詢(xún)墨坚,也就是like處理秧饮,推薦使用以下處理方式:
t_user.username like #username#
java代碼里:
if?(!StringUtil.isEmpty(this.username))?{
table.setUsername("%"?+?this.username +?"%");
}
或者也可以使用數(shù)據(jù)庫(kù)函數(shù)進(jìn)行連接處理:
select? * from t_user u where username? like CONCAT('%', #username#, '%')
注意:以上就可以發(fā)現(xiàn)在某些特定場(chǎng)景下只能用${},比如order by 后的排序字段框杜,表名浦楣、列名,因?yàn)樾枰鎿Q為不變的常量。如果表名中使用#{}的話咪辱,會(huì)變成如下:
select * from #{tablename}-->tablename傳參為t_user --->處理后變成 select * from 't_user'振劳,沒(méi)有這樣的表名,這樣的話就會(huì)報(bào)錯(cuò)了油狂,order by 同理历恐。
2)性能考慮
因?yàn)轭A(yù)編譯語(yǔ)句對(duì)象可以重復(fù)利用,把一個(gè)sql預(yù)編譯后產(chǎn)生的PreparedStatement對(duì)象緩存下來(lái)专筷,下次對(duì)于同一個(gè)sql弱贼,可以直接使用緩存的PreparedStatement對(duì)象,mybatis默認(rèn)情況下磷蛹,對(duì)所有的sql進(jìn)行預(yù)編譯吮旅,這樣的話#{}的處理方式性能會(huì)相對(duì)高些。
總結(jié):
能使用#{}的時(shí)候盡量使用#{}
表名味咳、order by的排序字段作為變量時(shí)庇勃,使用${}。
如果這篇文章對(duì)你有用的話槽驶,那就請(qǐng)拿起你高貴的手责嚷,輕輕的點(diǎn)擊喜歡吧,你喜歡我掂铐,我也喜歡你呀罕拂!