動態sql是mybatis的主要特性之一。在mapper中定義的參數傳到xml中之後,在查詢之前mybatis會對其進行動態解析。 mybatis提供了兩種支持動態sql的語法: {} 和 ${}。 username傳參一致的話,這兩種執行的結果是一樣的,但是這兩種方式在動態sql解析階段的處理是不 ...
動態sql是mybatis的主要特性之一。在mapper中定義的參數傳到xml中之後,在查詢之前mybatis會對其進行動態解析。
mybatis提供了兩種支持動態sql的語法:#{} 和 ${}。
select * from t_user where username = '${username}';
select * from t_user where username = #{username};
username傳參一致的話,這兩種執行的結果是一樣的,但是這兩種方式在動態sql解析階段的處理是不一樣的。
1、#{}
解析為一個JDBC預編譯語句(prepared statement)的參數標記符,把參數部分用占位符 ? 代替。動態解析為:
select * from t_user where username = ?;
而傳入的參數將會經過PreparedStatement方法的強制類型檢查和安全檢查等處理,最後作為一個合法的字元串傳入。
2、${}
這種方式只會做簡單的字元串替換,在動態SQL解析階段將會進行變數替換,假如傳遞的參數為二師兄,最終處理結果如下:
select * from t_user where username = '二師兄' ;
這樣在預編譯之前的sql語句已經不包含變數了,因此可以看出 ${} 變數的替換階段是在動態SQL解析階段。
3、#{}與${}兩種方式對比
1)是否預防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#
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 同理。
2)性能考慮
因為預編譯語句對象可以重覆利用,把一個sql預編譯後產生的PreparedStatement對象緩存下來,下次對於同一個sql,可以直接使用緩存的PreparedStatement對象,mybatis預設情況下,對所有的sql進行預編譯,這樣的話 #{}的處理方式性能會相對高些。
總結:
能使用#{}的時候儘量使用#{}表名。
order by的排序欄位作為變數時,使用${}。
來源:https://dwz.cn/tQwJGPbP
關註微信公眾號【悟能之能】瞭解更多編程技巧。
回覆"面經"獲取面試資料