MyBatis参数传递方式

  • 情况一:Mapper映射器接口方法参数只有一个且为基本类型

    接口方法:

    public List<UserEntity> selectUserByAge(int age);

    映射结果:

    <select id="selectUserByAge" resultMap="userResultMap">
    select * from tb_user where age > #{age};
    </select>

    其中 #{参数名} 表示参数占位符,等价于SQL语句的 号,这里的 #{age} 对应的就是接口方法 selectUserByAge 的参数。由于只有一个参数,而且是基本类型,所以写成 #{userAge} 或者 #{ageUser} 都无所谓,反正作用是一样的。

  • 情况二:Mapper映射器接口方法参数只有一个且为引用类型

    接口方法:

    public int insertUser(UserEntity user);

    映射结果:

    <insert id="insertUser">
    insert into tb_user (id,userName, password, name, age, sex, birthday, created, updated) values
    (null,#{userName},#{password},#{name},#{age},#{sex},#{birthday},now(),now());
    </insert>

    接口方法 insertUser 的参数是引用类型,其实传递给 SQL 语句的参数是引用类型的属性值,SQL 语句本身是不支持引用类型的。那引用类型有很多属性(或成员变量),是如何与 SQL 语句的参数一一对应的呢?

    答案是使用 #{引用类型的属性名} ,这里需要注意的是属性名不能写错了,否则就无法与 SQL 语句的参数对应,无法正确传递参数哈。

    public class UserEntity {
    private int id;
    private String userName;
    private String password;
    private String name;
    private int age;
    private int sex;
    private Date birthday;
    private String created;
    private String updated;
    }

    由于是自增主键,所以不需要传递引用类型的 id 参数,使用 null 代替,数据库会自动生成主键 id 标识。

  • 情况三:Mapper映射器接口方法参数有两个基本类型

    接口方法:

    public int updateUser(int id, String name);

    映射结果:

    <update id="updateUser">
    update tb_user set name=#{name} where id=#{id};
    </update>

    接口方法 updateUser 有两个参数且都是基本类型,按理说直接使用 #{参数名} 就可以了,不过一运行居然报错,如下:

    ### SQL: update tb_user set name=? where id=?;
    ### Cause: org.apache.ibatis.binding.BindingException: Parameter 'name' not found. Available parameters are [0, 1, param1, param2]

    从错误信息描述看,说是参数 name 没有发现,有效的参数是 [0, 1, param1, param2]。这是什么意思呀? 意思就是说当遇到不只一个参数时,比如两个参数,就不能用#{参数名}作为占位符,可以用MyBatis提供了两种方式之一。

    • 方式一#{0} 表示第一个参数 name,#{1} 表示第二个参数 id,#{2} 表示第三个参数...

      使用如下:

      <update id="updateUser">
      update tb_user set name=#{0} where id=#{1};
      </update>
    • 方式二#{param1} 表示第一个参数 name,#{param2} 表示第二个参数 id,#{param3} 表示第三个参数...

      使用如下:

    <update id="updateUser">
    update tb_user set name=#{param1} where id=#{param2};
    </update>

    其实,如果你非要用 #{参数名} 作为占位符,还可以用 MyBatis 提供的第三种方式,如下:

    • 方式三:给接口方法的参数取别名,只要参数别名和 #{参数名} 相同就可以了。

      使用如下:

    // @Param("id")表示给参数 int id 取别名为id,@Param("name") 表示给参数 name 取别名为name
    public int updateUser(@Param("id") int id,@Param("name") String name);
    <update id="updateUser">
    update tb_user set name=#{name} where id=#{id};
    </update>

    总结

    以上三种 MyBatis 参数的传递方式,哪种项目开发中比较常用呢?答案是第三种方式。理由是这种方式的代码可读性更好。想一想上面举例中,是#{name},#{id}作为参数占位符意思让人一目了然,还是#{0},#{1},#{param1},#{param2}呢?答案应该不言而喻。

  • 情况四:Mapper映射器接口方法参数有两个引用类型

    接口方法:

    public List<UserEntity> selectUserByAgeAndSex(@Param("userOne") UserEntity userOne,@Param("userTwo") UserEntity userTwo);

    映射结果1:

    <select id="selectUserByAgeAndSex" resultMap="userResultMap">
    select * from tb_user where age > #{userOne.age} and sex = #{userTwo.sex};
    </select>

    映射结果2:

    <select id="selectUserByAgeAndSex" resultMap="userResultMap">
    select * from tb_user where age > #{param1.age} and sex = #{param2.sex};
    </select>

    以上两种映射方式都可以,但是如果没有为两个参数取 @Param("userOne") 和 @Param("userTwo") 别名的话,那么就只有映射结果2可以了,映射结果1将会报错。

    爱思考的你可能会问,MyBatis 不是还有一种传参的方式吗?如下映射方式是否可以?

    <select id="selectUserByAgeAndSex" resultMap="userResultMap">
    select * from tb_user where age > #{0.age} and sex = #{1.sex};
    </select>

    回答这个问题很简单,试一试不就知道了嘛。测试结果如下:

    ### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter '0' not found. Available parameters are [userOne, userTwo, param1, param2]
    ### Cause: org.apache.ibatis.binding.BindingException: Parameter '0' not found. Available parameters are [userOne, userTwo, param1, param2]

    测试结果报错,这说明 #{0}、#{1}这种参数占位符的方式只适用于参数是基本类型,不适用于参数是引用类型。

  • 情况五:Mapper映射器接口方法参数有多个(包括基本类型和引用类型)

    接口方法:

    public List<UserEntity> selectUserByNameAndAge(@Param("name") String name, @Param("user") UserEntity user);

    映射结果1:

    <select id="selectUserByNameAndAge" resultMap="userResultMap">
    select * from tb_user where name = #{name} and age > #{user.age};
    </select>

    映射结果2:

    <select id="selectUserByNameAndAge" resultMap="userResultMap">
    select * from tb_user where name = #{param1} and age > #{param2.age};
    </select>

    以上两种映射方式都可以。

看到这里,相信你应该对 MyBatis 的#{ }传参方式已经胸有成竹了吧。无论接口方法的参数个数如何、类型如何,你应该都知道如何映射。

MyBatis 参数传递${}方式

MyBatis 除了可以使用 #{ } 方式传递参数,还有一种传参的方式,那么就是 ${ }。你可能会想,#{ }方式传递参数就已经够用了,干嘛还要搞一个 ${ } 出来,有完没完呀。我们还是先来看一下它的用法再说。

接口方法:

 public List<UserEntity> selectUserByNameAndAge(@Param("name") String name, @Param("user") UserEntity user);

映射结果:

<select id="selectUserByAgeAndSex" resultMap="userResultMap">
select * from tb_user where age > ${userOne.age} and sex = ${userTwo.sex};
</select>

原来这么简单,直接把原来的 #{ } 替换成 ${ }就可以了啦。那是不是所有 SQL 语句中使用 #{ } 的地方都可以被替换,这两种方式效果是相同的,是吗?对不起,回答错误。我们再看一个例子。

接口方法:

 public int updateUser(@Param("id") int id,@Param("name") String name);

映射结果:

<update id="updateUser">
update tb_user set name=${name} where id=${id};
</update>

运行测试,结果如下:

### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column '张三三' in 'field list'
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: update tb_user set name=张三三 where id=1;
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column '张三三' in 'field list'

报错了吧,这是怎么回事。从抛出的异常错误信息中,可以重点看 Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column '张三三' in 'field list' 这句话。首先Cause 的中文意思就是错误原因;接着告诉我们抛出了一个错误异常 MySQLSyntaxErrorException,这个异常是 SQL 语法错误的意思;最后提示我们引起 SQL 语法错误的是 Unknown column '张三三',中文意思是无法识别的表字段名‘张三三''。

分析了半天,还是不知道问题到底出在哪里?有点耐心好吧,你知道吗?经常有程序员前辈会告诉你,代码是调试出来的,不是写出来的,意思就是代码都是从错误中改出来的,没有人写代码从不出错。而作为一名合格的程序员,是需要具有独立解决问题的能力。那怎样才能具有独立解决问题的能力,这就需要多学一学人家是如何分析和定位问题的,逐渐积累经验哈。

接着我们再看下面这三句,也许会给我们更多错误提示信息:

### The error may involve defaultParameterMap 错误可能涉及默认的参数映射
### The error occurred while setting parameters 当设置参数时产生错误的
### SQL: update tb_user set name=张三三 where id=1; 有语法错误的 SQL 语句

我把这三句翻译成中文,相信你应该会看得明白一些。现在你应该感受到,要想编程好英文少不了。如果英文不过关,错误原因就在眼前,你也熟视无睹,如同盲人一般。所以,抽空补一补英文,别让它拖你的后腿,成为你通往程序员大牛的绊脚石。

现在错误原因已经搞清楚了,传递参数导致的 SQL 语法错误,具体而言是传递 name 参数。那究竟错在哪里呢?update tb_user set name=张三三 where id=1; 这句 SQL 语句语法错误在于参数'张三三'是字符串但是 SQL 语句中并没有加单引号。

对比以下正确和错误的 SQL 语法如下:

update tb_user set name=张三三 where id=1;   #错误 SQL 语法
update tb_user set name='张三三' where id=1; #正确 SQL 语法

现在你应该明白了, #{ } 和 ${ }用法还是有区别的。

${ } 会将传递的参数直接显示在 SQL 语句中,而 #{ } 会将传递的参数自动添加单引号。

现在可以解释为何第一个例子没有报错,第二个例子就报错了。因为第一个例子的两个参数都是 int 类型,所以加不加单引号都是一样的,也就是说,以下两条 SQL 语句执行结果相同。

select * from tb_user where age > '20' and sex = '1';# 采用#{}方式
select * from tb_user where age > 20 and sex = 1; # 采用${}方式

那 ${ } 在项目开发中到底有何用武之地呢?我们可以反过来想,什么情况下需要传递的参数不能自动添加单引号,否则会报错,而这些情况就是它的用武之地。

  • 情况一:order by 时,必须使用 ${ }

    什么意思呀,还是举个例子,如下:

    select * from tb_user where age > '20' order by 'age'
    select * from tb_user where age > '20' order by age

    以上两条 SQL 语句都可以在数据库中执行,但是只有一条 SQL 语句执行结果是正确的,请问是哪一条?还是动手试一试就知道了。

    执行第一条 SQL 语句结果如下:

    执行第二条 SQL 语句结果如下:

    我们知道 order by 是将查询结果进行排序,这里是按照年龄排序,默认是升序。这两条 SQL 语句执行结果只有第二条是正确的。

    现在你应该明白了,为何 order by 后面如果要传递参数,必须用不加单引号的 ${ },而不是自动加单引号的#{ }了吧。

  • 情况二:表名作为参数时,必须使用 ${ }

    什么时候会用表名做参数呀,那就是当数据库有两个一模一样的表,分别是历史表和当前表。历史表和当前表都可以查询表中的信息,但有时候需要从历史表中去查询数据,有时候需要从当前表中查询数据,而且希望使用1个方法来完成查询操作,如下:

    select * from ${tableName}

    如果表名作为参数使用 #{ } 那么就会给表名自动添加单引号,这明显 SQL 语法不正确,这就是为何如果参数传递是表名时只能用 ${ }。

MyBatis 参数传递 #{} 和 ${} 区别

想必 MyBatis 的#{ } 和 ${ } 两种参数传递的方式你已经掌握了,那么我想再进一步加深你对它们的理解。你要明白 MyBatis 框架本质上是对 JDBC 的封装,所以想要深入理解 MyBatis 的原理,需要对 JDBC 有深入的认识。

  • 原理

    #{ }:为参数占位符?(即底层使用了 JDBC 的 PreparedStatement 来进行预处理)

    ${ }:为字符串替换(即底层使用了 JDBC 的 Statement 直接进行查询)

    注:如果你对 JDBC 的 PreparedStatement 和 Statement 的两种参数处理方式不了解,建议自己去补一补 JDBC 相关的内容。

  • 参数传递

    #{ }: 传递参数后 SQL 语句自动为参数加上单引号

    ${ }: 传递参数后 SQL 语句不会为参数加上单引号

  • SQL 注入

    #{ }:可以防止 sql 注入

    ${ }:不可以防止 sql 注入

    注:SQL 注入是黑客攻击服务器的一种简单手段,感兴趣的话可以去看我写的关于 SQL 注入的详细博客文章。

MyBatis 参数传递总结

参数传递方式

  • 参数传递 #{ } 方式:#{0} 、 #{param1} 、 @param(别名) / #{参数名}
  • 参数传递 ${ } 方式:用法同 #{ } 相同,注意与 #{ } 的区别

项目开发建议

  • 建议接口参数一律使用 @param("参数名") 为参数取别名(可读性高)
  • 建议只要能用 #{ } 的地方尽量不使用 ${ }(安全性高)

MyBatis 各种参数传递方式的更多相关文章

  1. 【整理】--C++三种参数传递方式

    在C++中,共有三种参数传递方式: 按值传递(pass by value) 地址传递(pass by pointer) 引用传递(pass by reference) (1)按值传递的过程为:首先计算 ...

  2. 基于.Net Framework 4.0 Web API开发(2):ASP.NET Web APIs 参数传递方式详解

    概述:  ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作.调用API过程中参数的传递是必须的,本节就来谈谈 ...

  3. mybatis 传递参数的方法总结

    有三种mybatis传递参数的方式: 第一种 mybatis传入参数是有序号的,可以直接用序号取得参数 User selectUser(String name,String area); 可以在xml ...

  4. 新手容易混乱的String+和StringBuffer,以及Java的方法参数传递方式。

    之前在交流群里和猿友们讨论string+和stringbuffer哪个速度快以及Java的方法参数传递的问题,引起了群里猿友的小讨论.最终LZ得出的结果是string+没有stringbuffer快, ...

  5. 【转载】Mybatis多参数查询映射

    转载地址:http://www.07net01.com/zhishi/402787.html 最近在做一个Mybatis的项目,由于是接触不久,虽然看了一下资料,但在实际开发中还是暴 露了很多问题,其 ...

  6. Java 函数参数传递方式详解 分类: Java Game 2014-08-15 06:34 82人阅读 评论(0) 收藏

    转:http://zzproc.iteye.com/blog/1328591 在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式:  A. 是按值传递的?  B. ...

  7. Java函数参数传递方式详解

    在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式: A. 是按值传递的? B. 按引用传递的? C. 部分按值部分按引用? 此处暂不宣布正确答案,我们通过一个简 ...

  8. 产品经理学Python:参数传递方式

    这是关于Python的第5篇文章,主要介绍下参数传递方式和如何设计自己的函数. (一) 本篇主要介绍2种参数传递方式. 位置参数 调用函数时,根据函数定义的参数位置来传递参数. def right_t ...

  9. Python 关于Python函数参数传递方式的一点探索

    关于Python函数参数传递方式的一点探索 by:授客 QQ:1033553122 实践代码 #!/usr/bin/env python # -*- coding:utf-8 -*- __author ...

随机推荐

  1. 【CTF】CTFHub 技能树 文件头检查 writeup

    PHP一句话木马 <?php @eval($_POST["pass"]);?> <?php eval($_REQUEST["pass"]);? ...

  2. Python 高级进阶知识(一)

    参考 Python学习手册 第四版 1 from vs import import 模块 : 导入的一整个模块(python中模块对应一个py文件) 因为import使用一个变量名引用整个模块对象,所 ...

  3. 这一次,彻底搞懂 Go Cond

    hi,大家好,我是 haohongfan. 本篇文章会从源码角度去深入剖析下 sync.Cond.Go 日常开发中 sync.Cond 可能是我们用的较少的控制并发的手段,因为大部分场景下都被 Cha ...

  4. JAVAEE_Servlet_08_HTTP状态码以及错误页面设置

    HTTP协议状态码 * HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用, HTTP状态码共分为5种类型: - 1** 信息,服务器收到请求,需要请求者 ...

  5. JVM小册(1)------jstat和Parallel GC日志

    JVM小册(1)------jstat和Parallel GC日志 一. 背景 在生产环境中,有时候会遇到OOM的情况,抛开Arthas 等比较成熟的工具以外,我们可以使用java 提供的jatat和 ...

  6. 详谈lastIndex对正则结果的影响

    前言 今天遇到一个问题,用正则表达式去检查同一个字符串时,交替返回true和false.无奈之下,重新翻了翻权威指南,发现罪魁祸首原来是lastIndex.可在控制台尝试下 ? 1 2 3 4 5 6 ...

  7. SpringCloud之远程调用OpenFeign和Ribbon

    Ribbon.Feign和OpenFeign的区别 SpringCloudAlibaba微服务实战教程系列 Spring Cloud 微服务架构学习记录与示例 一 简介 Feign是Netflflix ...

  8. hdu4869 费马小+快速幂

    思路:费马小+快速幂       无论怎么翻,每一步的1出现的可能个数的奇偶性是一样的,因为奇数 - 偶数 = 奇数,偶数 - 偶数 = 偶数,有一张牌被重叠了,那么就减去一个偶数2,所以怎么重叠都不 ...

  9. hook Android系统调用的乐趣和好处

    翻译:myswsun 0x00 前言 Android的内核是逆向工程师的好伙伴.虽然常规的Android应用被限制和沙盒化,逆向工程师可以按自己希望自定义和改变操作系统和内核中行为.这给了你不可多得的 ...

  10. Windows核心编程笔记之作业

    创建作业,并加以限制 HANDLE WINAPI CreateJob() { BOOL IsInJob = FALSE; DWORD ErrorCode = NULL; // 不能将已经在作业中的进程 ...