MyBatis–#与$的区别

发布时间:2023年12月18日

MyBatis–#与$的区别

简介

主要介绍MyBatis中#与$的区别以及优劣势。

区别

#{}${}
编译器行为在预编译期,生成?,作为占位符在预编译期,会直接显示拼接的字符
拼接行为如果字段是字符串类型,拼接时会在字符串两侧添加单引号无论是什么类型,拼接时不会在两侧添加单引号

情况一:一个用 ${} 一个用 #{}

<select id="getUserByNameAndPsw" resultType="map">
        select * from USER where userName=${userName} and userPassword =#{userPassword};
  </select>

结果

==> Preparing: select * from USER where userName=mww and userPassword =?;
==> Parameters: 123(String)

结论:

很显然: ${} 是直接拼成字符串的 ,#{} 是生成 ?占位符的。而且因为 userName:mww 是字符串,所以 这种写法显然也是错误的 ,会报出如下错误:### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column ‘mww’ in ‘where clause’。把mww当成一个变量了。

所以正确的写法是这样的:为字符串字段加上单引号 ’ ’

<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select * from USER where userName='${userName}' and userPassword =#{userPassword};
</select>

结果:

==>  Preparing: select * from USER where userName='mww' and userPassword =?; 
==>  Parameters: 123(String)
<==  Total: 1

结论:

显然这种写法是正确的,从这里可以看出,预编译期 ${} 是直接把参数拼结到SQL中,运行时,就只传入了一个 #{} 修饰的参数。

情况二: ${} SQL注入


<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select * from USER where userName='${userName}' and userPassword =#{userPassword};
  </select>

结果:

 ==>  Preparing: select * from USER where userName='' OR 1=1 OR '' and userPassword =?; 
 ==>  Parameters: 65787682342367(String)
 <==  Total: 2

结论:

只要我们在参数中输入 ‘ OR 1=1 OR ‘ 无论后面的密码输入什么都可以,查询到数据,这种情况就是SQL注入。

情况三:#{} 防止SQL注入


<select id="getUserByNameAndPsw" parameterType="map" resultType="com.hotel3.model.User">
        select * from USER where userName=#{userName} and userPassword =#{userPassword};
  </select>

结果:

 ==>  Preparing: select * from USER where userName=? and userPassword =?; 
 ==>  Parameters: ' OR 1=1 OR '(String), 123(String)
 <==  Total: 0 

结论:

上面预编译SQL的参数已经由占位符 { ?} 代替,所以传入的 ‘ OR 1=1 OR ‘ 只会作为 userName字段的值,而不会拼入执行的SQL。这样就达到了防止SQL注入的目的。


${}正确用法

1、同时传入一个字段名和字段值

User u=userService.getUserByNameAndPsw(“userName,userType,userPassword”,userName,userPassword);
SQL: select ${arg0} from USER

  <select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select ${arg0} from USER where userName=#{userName} and userPassword =#{userPassword};
  </select>

结果:

 ==>  Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; 
 ==>  Parameters: mww(String), 123(String)
 <==  Total: 1

结论:

生成了我们想要SQL语句 :select userName,userType,userPassword from USER…


2、传入两个字段名和字段值

User u=userService.getUserByNameAndPsw(“userName,userType,userPassword”,userName,userName,userPassword);

SQL:  select ${arg0} from USER
<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select ${arg0} from USER where ${arg1}=#{userName} and userPassword =#{userPassword};
  </select>

结果:

 ==>  Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; 
 ==>  Parameters: mww(String), 123(String)
 <==  Total: 1

结论:

按照传参的顺序匹配 SQL 中的 {arg1} 生成我们想要的代码,但这个方式会使Mybatis 的 Mapper 文件可读性变差,如果不看其他的代码,很难辨别 arg0和arg1代表的含义。

3、使用Map传值,提高 Mapper 文件的可读性

Map map =new HashMap();
map.put(“selectValues”,“userName,userType,userPassword”);
map.put(“userNamefieId”,“userName”);
map.put(“userName”,userName);
map.put(“userPassword”,userPassword);
User u=userService.getUserByNameAndPsw(map);
Mapper 文件的 xml

  <select id="getUserByNameAndPsw" parameterType="map" resultType="com.hotel3.model.User">
        select ${selectValues} from USER where ${userNamefieId}=#{userName} and userPassword =#{userPassword};
  </select>

结果:

 ==>  Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; 
 ==>  Parameters: mww(String), 123(String)
 <==  Total: 1

我现在用的也是作者写的第三种方式。使用map传值,resultType也使用map,好写好用可读性强,还能避免sql注入的问题。

文章来源:https://blog.csdn.net/weixin_42662955/article/details/134973561
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。