mybatis中$和#的區別及應用場景


#:預編譯

解析為一個JDBC預編譯語句(prepared statement)的參數標記符,把參數部分用占位符代替。動態解析為:

select * from t_user where username = ? ;

而傳入的參數將會經過PreparedStatement方法的強制類型檢查和安全檢查等處理,最后作為一個合法的字符串傳入。

$:簡單替換

在動態SQL解析階段將會進行變量替換,假如傳遞的參數為Alice,最終處理結果如下:

select * from t_user where username = 'Alice' ;

這樣在預編譯之前的sql語句已經不包含變量了,因此可以看出${} 變量的替換階段是在動態SQL解析階段。

對比

是否預防SQL注入*

以上不同的處理方式可以看出,#{}預處理之后可以預防SQL注入;而${}在預編譯之前就已經被替換,有被注入的風險,如下例:

如果傳入的username 為 a' or '1=1,那么使用${}處理后直接替換字符串的sql就解析為:

select * from t_user where username = 'a' or '1=1' ;

這樣的話所有的用戶數據就被查出來了,這樣就屬於SQL注入。

如果使用#{},經過sql動態解析和預編譯,會把單引號轉義為'

那么sql最終解析為:

select * from t_user where username = "a\' or \'1=1 ";

//這樣會查不出任何數據,有效阻止sql注入

有的業務場景經常用到模糊查詢,也就是like處理,推薦使用以下處理方式:

t_user.username like #username#

java代碼里:

 
 

if (!StringUtil.isEmpty(this.username)) {

table.setUsername("%" + this.username + "%");

}

或者也可以使用數據庫函數進行連接處理:

select * from t_user u where username like CONCAT('%', #username#, '%')

注意:以上就可以發現在某些特定場景下只能用${},比如order by 后的排序字段,表名、列名,因為需要替換為不變的常量。如果表名中使用#{}的話,會變成如下:

select * from #{tablename}–>tablename傳參為t_user—>處理后變成 select * from 't_user',多了單引號,沒有這樣的表名,這樣的話就會報錯了,order by同理。

因此,${}比較特殊, 他的應用場景是 需要動態傳入 表名或列名時使用。

性能考慮

因為預編譯語句對象可以重復利用,把一個sql預編譯后產生的PreparedStatement對象緩存下來,下次對於同一個sql,可以直接使用緩存的PreparedStatement對象,mybatis默認情況下,對所有的sql進行預編譯,這樣的話#{}的處理方式性能會相對高些。

應用場景

能使用#{}的時候盡量使用#{}

表名、order by的排序字段作為變量時,使用${}。

源自:https://www.senup.cn/p/1b99.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM