[Java MyBatis] #{}与${}的使用与区别(详细)


前言

        最近在开始思考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=?

${}是字符串拼接,多数情况下使用与#{}没区别,但是有下面几点要注意:

  1. 如果这个列是字符类型,我们要加引号,如where username='${username}'
  2. 如果传入参数是简单类型像是数字、字符串等要在UserDao.java中加@Param("username")否则${}取值会有问题
  3. 如果是一个引用类型的实例对象参数,那我们可以不加,如:
    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片段上进行动态填充,那我们必须用 ${ }来取值。


资料参考

https://www.bilibili.com/video/BV1gC4y1p7z2?p=845

posted @ 2021-01-04 19:10  AzureSky_X  阅读(766)  评论(0编辑  收藏  举报