A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© sjj632605 中级黑马   /  2019-2-19 16:11  /  2523 人查看  /  2 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 sjj632605 于 2019-2-21 15:15 编辑
一、举例说明
[XML] 纯文本查看 复制代码
1 select * from user where name = "zhangsan"; 
2 
3 select * from user where name = #{name}; 
4 
5 select * from user where name = '${name}'; 

     一般情况下,我们都不会注意到这里面有什么不一样的地方。因为这些sql都可以达到我们的目的,去查询名字叫dato的用户。
二、区别
     动态 SQL mybatis 的强大特性之一,也是它优于其他 ORM 框架的一个重要原因。mybatis 在对 sql 语句进行预编译之前,会对 sql 进行动态解析,解析为一个 BoundSql 对象,也是在此处对动态 SQL 进行处理的。在动态 SQL 解析阶段, #{ } ${ } 会有不同的表现
[XML] 纯文本查看 复制代码
select * from user where name = #{name}; 
#{} 在动态解析的时候, 会解析成一个参数标记符。就是解析之后的语句是:
[XML] 纯文本查看 复制代码
select * from user where name = ?; 
那么我们使用 ${}的时候
[XML] 纯文本查看 复制代码
select * from user where name = '${name}'; 
${}在动态解析的时候,会将我们传入的参数当做String字符串填充到我们的语句中,就会变成下面的语句
[XML] 纯文本查看 复制代码
select * from user where name = "zhangsan"; 
   预编译之前的 SQL 语句已经不包含变量了,完全已经是常量数据了。相当于我们普通没有变量的sql了。
综上所得, ${ } 变量的替换阶段是在动态 SQL 解析阶段,而 #{ }变量的替换是在 DBMS 中。
这是 #{} 和 ${} 我们能看到的主要的区别,除此之外,还有以下区别:
  • #方式能够很大程度防止sql注入。
  • $方式无法防止Sql注入。
  • $方式一般用于传入数据库对象,例如传入表名.
  • 一般能用#的就别用$.
  所以我们在使用mybatis的时候,尽量的使用#方式!!!这是大家要注意的地方
三、传参说明
   在UserMapper接口中,添加根据表名查询用户信息的方法:

[XML] 纯文本查看 复制代码
/**
* 通过用户名查询用户
* @param tableName
* @return
*/
List<User> queryUserListByTableName(String tableName);

第二步:编写一个查询的statement
  在UserMapper映射文件中,添加方法对应的statement:

[XML] 纯文本查看 复制代码
<!-- 通过表名查询数据 -->
<select id="queryUserListByTableName" resultType="User">
   select * from ${tableName}
</select>

第三步:编写测试方法

[Java] 纯文本查看 复制代码
@Test
public void testQueryUserListByTableName(){
    List<User> list = userMapper.queryUserListByTableName("tb_user");
    System.out.println(list);

}

执行方法输出(报错):只能使用${value}来传递参数,其他任何字符串都不行
file:///C:\Users\SHENJI~1\AppData\Local\Temp\ksohtml\wps465.tmp.jpg
如果你要动态传入的字段名是表名,并且sql执行是预编译的,这显然是不允许的,所以你必须改成非预编译的,也就是这样:
在一个参数的情况下必须,参数名称必须为value:即${value}
[XML] 纯文本查看 复制代码
<!-- 通过表名查询数据 -->
<select id="queryUserListByTableName" resultType="User">
   select * from ${value}
</select>

注意:
使用${} 去接收参数信息,在一个参数时,默认情况下必须使用${value}获取参数值,

但是这并不是一种稳妥的解决方案,推荐使用@Param注解指定参数名.
接口修改如下:

[Java] 纯文本查看 复制代码
/**
* 通过用户名查询用户
* @param tableName
* @return
*/
List<User> queryUserListByTableName([color=#000000]@Param("tableName")[/color][color=#ff0000] [/color]String tableName);
映射文件修改如下:

[XML] 纯文本查看 复制代码
<!-- 通过表名查询数据 -->
<select id="queryUserListByTableName" resultType="User">
   select * from ${tableName}
</select>
#{}的用法
#{}类似于sql语句中的?,但是单个参数和多个参数的使用方式稍微有点区别。
一、单个参数
  参见之前的通过id查询数据的statement:

[XML] 纯文本查看 复制代码
<select id="queryUserById" resultType="User">
       select * from tb_user where id = #{id}
   </select>


执行查询结果如下:#{}就相当于一个?,可以进行预编译的占位。

注意:在一个参数的情况下#{id}中的id可以修改成任意字符。
即使修改如下,也可以照常运行:

[XML] 纯文本查看 复制代码
<select id="queryUserById" resultType="User">
       select * from tb_user where id = #{xxx}
</select>


二、多个参数
  当mapper接口要传递多个参数时,有两种传递参数的方法:
1、 默认规则获取参数{0,1,param1,param2}
2、 使用@Param注解指定参数名

案例:实现一个简单的用户登录,根据username和password验证用户信息
UserMapper接口中,添加登陆方法:

[Java] 纯文本查看 复制代码
/**
* 用户登陆
* @param userName
* @return
*/
User login(String userName,String password);

UserMapper.xml配置中,添加登陆方法对应的Statement配置:

[XML] 纯文本查看 复制代码
<select id="login" resultType="User">
   select * from tb_user where user_name = #{userName} and password = #{password}
</select>

UserMapperTest测试用例中,添加测试方法:

[Java] 纯文本查看 复制代码
@Test
public void testLogin(){
    User user = userMapper.login("zhangsan", "123456");
    System.out.println(user);
}

运行报错:
注意报错信息:没有找到参数userName,可用的参数是:[1,0,param1,param2],所以可有以下解决方案:
解决方案一:
在映射文件里,通过#{0},#{1}获取参数

[XML] 纯文本查看 复制代码
<!--
   通过参数的索引号传递参数:0,1,2依次类推
-->
<select id="login" resultType="User">
   select * from tb_user where user_name = #{0} and password = #{1}
</select>

解决方案二:
在映射文件里,通过param1,param2获取参数

[XML] 纯文本查看 复制代码
<!--
   通过参数的索引号传递参数:0,1,2依次类推
   通过参数的固定名称传递参数:param1,param2,param3依次类推
-->
<select id="login" resultType="User">
   select * from tb_user where user_name = #{param1} and password = #{param2}
</select>

最终解决方案:
在接口方法中的参数前,添加@Param注解指定参数名:

[Java] 纯文本查看 复制代码
/**
* 用户登陆
* @param userName
* @return
*/
User login([color=#ff0000]@Param("userName")[/color] String userName,[color=#ff0000]@Param("password") [/color]String password);
映射文件中修改如下:

[XML] 纯文本查看 复制代码
<!--
   通过参数的索引号传递参数:0,1,2依次类推
   通过参数的固定名称传递参数:param1,param2,param3依次类推
-->
<select id="login" resultType="User">
   select * from tb_user where user_name = #{userName} and password = #{password}
</select>

注意:
单个参数时,#{}与参数名无关的。
多个参数时,#{}和${}与参数名(@Param)有关。

什么时候需要加@Param注解?什么时候不加?
单个参数不加,多个参数加
终极解决方案:都加。


2 个回复

倒序浏览
。。。。。。
回复 使用道具 举报
一个人一座城0.0 来自手机 中级黑马 2019-2-21 09:13:52
藤椅
看一看。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马