MyBatis 各种参数传递方式
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 各种参数传递方式的更多相关文章
- 【整理】--C++三种参数传递方式
在C++中,共有三种参数传递方式: 按值传递(pass by value) 地址传递(pass by pointer) 引用传递(pass by reference) (1)按值传递的过程为:首先计算 ...
- 基于.Net Framework 4.0 Web API开发(2):ASP.NET Web APIs 参数传递方式详解
概述: ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作.调用API过程中参数的传递是必须的,本节就来谈谈 ...
- mybatis 传递参数的方法总结
有三种mybatis传递参数的方式: 第一种 mybatis传入参数是有序号的,可以直接用序号取得参数 User selectUser(String name,String area); 可以在xml ...
- 新手容易混乱的String+和StringBuffer,以及Java的方法参数传递方式。
之前在交流群里和猿友们讨论string+和stringbuffer哪个速度快以及Java的方法参数传递的问题,引起了群里猿友的小讨论.最终LZ得出的结果是string+没有stringbuffer快, ...
- 【转载】Mybatis多参数查询映射
转载地址:http://www.07net01.com/zhishi/402787.html 最近在做一个Mybatis的项目,由于是接触不久,虽然看了一下资料,但在实际开发中还是暴 露了很多问题,其 ...
- Java 函数参数传递方式详解 分类: Java Game 2014-08-15 06:34 82人阅读 评论(0) 收藏
转:http://zzproc.iteye.com/blog/1328591 在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式: A. 是按值传递的? B. ...
- Java函数参数传递方式详解
在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式: A. 是按值传递的? B. 按引用传递的? C. 部分按值部分按引用? 此处暂不宣布正确答案,我们通过一个简 ...
- 产品经理学Python:参数传递方式
这是关于Python的第5篇文章,主要介绍下参数传递方式和如何设计自己的函数. (一) 本篇主要介绍2种参数传递方式. 位置参数 调用函数时,根据函数定义的参数位置来传递参数. def right_t ...
- Python 关于Python函数参数传递方式的一点探索
关于Python函数参数传递方式的一点探索 by:授客 QQ:1033553122 实践代码 #!/usr/bin/env python # -*- coding:utf-8 -*- __author ...
随机推荐
- 【CTF】CTFHub 技能树 文件头检查 writeup
PHP一句话木马 <?php @eval($_POST["pass"]);?> <?php eval($_REQUEST["pass"]);? ...
- Python 高级进阶知识(一)
参考 Python学习手册 第四版 1 from vs import import 模块 : 导入的一整个模块(python中模块对应一个py文件) 因为import使用一个变量名引用整个模块对象,所 ...
- 这一次,彻底搞懂 Go Cond
hi,大家好,我是 haohongfan. 本篇文章会从源码角度去深入剖析下 sync.Cond.Go 日常开发中 sync.Cond 可能是我们用的较少的控制并发的手段,因为大部分场景下都被 Cha ...
- JAVAEE_Servlet_08_HTTP状态码以及错误页面设置
HTTP协议状态码 * HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用, HTTP状态码共分为5种类型: - 1** 信息,服务器收到请求,需要请求者 ...
- JVM小册(1)------jstat和Parallel GC日志
JVM小册(1)------jstat和Parallel GC日志 一. 背景 在生产环境中,有时候会遇到OOM的情况,抛开Arthas 等比较成熟的工具以外,我们可以使用java 提供的jatat和 ...
- 详谈lastIndex对正则结果的影响
前言 今天遇到一个问题,用正则表达式去检查同一个字符串时,交替返回true和false.无奈之下,重新翻了翻权威指南,发现罪魁祸首原来是lastIndex.可在控制台尝试下 ? 1 2 3 4 5 6 ...
- SpringCloud之远程调用OpenFeign和Ribbon
Ribbon.Feign和OpenFeign的区别 SpringCloudAlibaba微服务实战教程系列 Spring Cloud 微服务架构学习记录与示例 一 简介 Feign是Netflflix ...
- hdu4869 费马小+快速幂
思路:费马小+快速幂 无论怎么翻,每一步的1出现的可能个数的奇偶性是一样的,因为奇数 - 偶数 = 奇数,偶数 - 偶数 = 偶数,有一张牌被重叠了,那么就减去一个偶数2,所以怎么重叠都不 ...
- hook Android系统调用的乐趣和好处
翻译:myswsun 0x00 前言 Android的内核是逆向工程师的好伙伴.虽然常规的Android应用被限制和沙盒化,逆向工程师可以按自己希望自定义和改变操作系统和内核中行为.这给了你不可多得的 ...
- Windows核心编程笔记之作业
创建作业,并加以限制 HANDLE WINAPI CreateJob() { BOOL IsInJob = FALSE; DWORD ErrorCode = NULL; // 不能将已经在作业中的进程 ...