前言
最近在開始思考MyBatis中的一些細節,遇到不會的就找博客,發現這部分內容有所欠缺。雖然在這條路上我還是個新手,但每次遇到問題最常幫助我的都是這些陌生人的博客,這次就由我來吧。歡迎轉載,請標明出處。有名字就更好了
#{}與${}的使用
#{}是占位符填充,如下當我們執行sql語句時會將sql語句中的 #{id} 替換成 ?號
UserDao.java 接口文件:
User queryUserByUsername(@Param("username") String username);
UserDaoMapper.xml 映射文件:
<select id="queryUserByUsername" resultMap="user_resultMap">
select id,username,password,gender,regist_time
from t_user
where username=#{username}
</select>
執行后的sql語句:
select id,username,password,gender,regist_time
from t_user
where username=?
${}是字符串拼接,多數情況下使用與#{}沒區別,但是有下面幾點要注意:
- 如果這個列是字符類型,我們要加引號,如where username='${username}'
- 如果傳入參數是簡單類型像是數字、字符串等要在UserDao.java中加@Param("username")否則${}取值會有問題
- 如果是一個引用類型的實例對象參數,那我們可以不加,如:
User queryUserByUsername(@Param("username") User user);
存在問題
使用${}當拼接sql片段時,有sql注入風險,外界參數會改變原有sql的語義,如:
//sql注入
String username = "zhangsan' or '1'='1";
注入后的sql語句:
select id,username,password,gender,regist_time
from t_user
where username='zhangsan' or '1'='1'
這就導致了哪怕你數據庫中沒有這個用戶名也能成功查詢到數據。而使用 #{ } 它會將sql語義過濾,將 zhangsan' or '1'='1 當成一個普通字符串,可以規避sql注入的風險。所以原則上能不用sql拼接就不用sql拼接,那為什么還要有 ${ } 的存在呢?是因為在有些場景中 #{ } 不能使用,如我們要給查詢到的數據進行排序
String rule="desc";
String sql="select * from t_user order by id ?";
當我們在占位符上填充desc時,會導致sql語句語法出錯,這就涉及了占位符的使用原則
//原則:填充數據,要和列相關
select * from t_user where id=?
inseret into t_user values(?,?,?)
update t_user set username?,password=?
顯然desc與列無關,就會導致報錯
小技巧
MyBatisTest.java 測試方法
@Test
public void test(){
UserDao mapper = MyBatisUtil.getMapper(UserDao.class);
Integer sig=0; //0:desc 1:asc
if(sig==0){
mapper.queryUserUsers("desc");
}else{
mapper.queryUserUsers("asc");
}
}
使用 ${ } 拼接sql片段的時候,用戶傳的內容只是一個依據,我們可以通過這個依據進行判斷,這時我們就能拼接自己定義的內容,而不是用戶輸入的內容。這樣即使我們拼接了語句也不會被注入,所以這是一個使用 ${ } 時非常重要的小技巧
優勢與劣勢
優勢 | 劣勢 | |
---|---|---|
${ } 字符串拼接 | 可以隨意拼接 | 有sql注入的風險 |
#{ } 占位符 | 規避sql注入風險 | 要和列相關的位置才可以使用 |
總結
當我們想要去取值時,能用 #{ }就盡量用 #{ },什么時候不能用 #{ }呢?如果這個位置不是為某個列做值的相關,而是要在某些sql片段上進行動態填充,那我們必須用 ${ }來取值。