技术框架中MyBatis参数传递的学习

news/2024/12/4 20:30:47/文章来源:https://www.cnblogs.com/BingBing-8888/p/18587117

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 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 没有发现,有效的参数是 [arg0, arg1, param1, param2]。这是什么意思呀? 意思就是说当遇到不只一个参数时,比如两个参数,就不能用 #{参数名} 作为占位符,可以用 MyBatis 提供了两种方式之一。

  • 方式一#{arg0} 表示第一个参数 name,#{arg1} 表示第二个参数 id,#{arg2} 表示第三个参数...

    使用如下:

    <update id="updateUser">update tb_user set name=#{arg0} where id=#{arg1};
    </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>

sql 中使用 arg0,arg1,arg..param1、param2、param... 这种方式来引用多参数,对参数的顺序依赖性特别强,如果有人把参数的顺序调整了或者调整了参数的个数,后果就是灾难性的,所以这两种方式不建议大家使用。

推荐大家使用第三种方式,不仅可读性高,而且参数顺序可以任意调整。

情况三:Mapper 映射器接口方法参数为 Map 类型

接口方法:

public List<UserEntity> selectByMap(Map<String,Object> map);

映射结果:

<select id="selectByMap" resultType="entity.UserEntity"><![CDATA[SELECT * FROM tb_user WHERE age=#{age} OR sex = #{sex}]]>
</select>
情况四: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 标识。

传递 java 对象的方式相对于 map 的方式更清晰一些,可以明确知道具体有哪些参数,而传递 map,我们是不知道这个 map 中具体需要哪些参数的,map 对参数也没有约束,参数可以随意传,建议多个参数的情况下选择通过 java 对象进行传参

情况五: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 > #{arg0.age} and sex = #{arg1.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]

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

情况六: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 的#{ }传参方式已经胸有成竹了吧。无论接口方法的参数个数如何、类型如何,你应该都知道如何映射。

情况七:Mapp 映射器接口方法参数为 Collection 类型

当传递的参数类型是Collection的时候,会被放在 map 中,key为collection,value 为参数的值

接口方法:

public List<UserEntity> selectByCollection(Collection<Integer> ages);

映射结果:

<select id="selectByCollection" resultType="entity.UserEntity"><![CDATA[SELECT * FROM tb_user WHERE age in (#{list[0]},#{list[1]})]]>
</select>
Mybatis 中集合参数处理了源码解析

集合参数,mybatis会进行一些特殊处理,代码在下面的方法中:

org.apache.ibatis.session.defaults.DefaultSqlSession#wrapCollection

源码解释:

判断参数是否是java.util.Collection类型,如果是,会放在map中,key为collection。如果参数是java.util.List类型的,会在map中继续放一个list作为key来引用这个对象。如果参数是数组类型的,会通过array来引用这个对象。

情况八:Mapper 映射器接口方法参数为 ResultHandler 类型

查询的数量比较大的时候,返回一个 List 集合占用的内存还是比较多的,比如我们想导出很多数据,实际上如果我们通过jdbc的方式,遍历ResultSetnext方法,一条条处理,而不用将其存到 List 集合中再取处理。

mybatis中也支持我们这么做,可以使用ResultHandler对象,犹如其名,这个接口是用来处理结果的,先看一下其定义:

public interface ResultHandler<T> {void handleResult(ResultContext<? extends T> resultContext);
}

里面有1个方法,方法的参数是ResultContext类型的,这个也是一个接口,看一下源码:

public interface ResultContext<T> {T getResultObject();int getResultCount();boolean isStopped();void stop();
}

4 个 方法:

  • getResultObject:获取当前行的结果
  • getResultCount:获取当前结果到第几行了
  • isStopped:判断是否需要停止遍历结果集
  • stop:停止遍历结果集

ResultContext接口有一个实现类org.apache.ibatis.executor.result.DefaultResultContext,mybatis中默认会使用这个类。

遍历tb_user表的所有记录,第 2 条遍历结束之后,停止遍历

接口方法:

void getList(ResultHandler<UserEntity> resultHandler);

映射结果:

<select id="getList" resultType="entity.UserEntity"><![CDATA[SELECT * FROM tb_user]]>
</select>

测试代码:

@Test
public void getListTest() {userMapper.getList(context->{//将context参数转换为DefaultResultContext对象DefaultResultContext<UserEntity> defaultResultContext= (DefaultResultContext<UserEntity>) context;//打印遍历结果System.out.println(defaultResultContext.getResultObject());//遍历到第二条之后停止if (defaultResultContext.getResultCount() == 2) {//调用stop方法停止遍历,stop方法会更新内部的一个标志,置为停止遍历defaultResultContext.stop();}});
}

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

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

  • ${ }:为字符串替换(即底层使用了 JDBC 的 Statement 直接进行查询)
参数传递
  • { }: 传递参数后 SQL 语句自动为参数加上单引号

  • ${ }: 传递参数后 SQL 语句不会为参数加上单引号
SQL 注入
  • { }:可以防止 sql 注入

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

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

MyBatis 参数传递总结

参数传递方式

  • 参数传递 #{ } 方式:#{arg0} 、 #{param1} 、 @param(别名) / #
  • 参数传递 ${ } 方式:用法同 #{ } 相同,注意与 #{ } 的区别
项目开发建议
  • 建议接口参数一律使用 @param("参数名") 为参数取别名(可读性高)
  • 建议只要能用 #{ } 的地方尽量不使用 ${ }(安全性高)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/846829.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电感的通低频阻高频特性的测试

用手持电桥测出电感的实际值,以保证在这个频率附近电感数值不会有太大变化 10k档     40k档   100k档 9.96mH    9.77mH   17.25mH 用示波器的信号发生器产生一个200k的信号,串接以上电感,查看输出波形当信号为200k时输出波形的峰峰值为1.55V(不接电感时为2.44…

whk1

选 \(B\)。

web19([GYCTF2020]Blacklist):

1.输入1回显出语法错误(找到注入点,是字符型) error 1064 : You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 1 at line 12.依次输入1 order by 3#、1 order by 1#、1 order by 2#…

linux,ssh连接

Linux,ssh远程连接 一、 linux端配置 1、安装ssh服务 sudo apt-get updatesudo apt-get install openssh-clientsudo apt-get install openssh-server2、启动ssh service ssh start3、检查是否成功启动ssh ps -e | grep ssh确保出现ssh-agent ,若无ssh-agent,执行下列代码 e…

【攻防技术系列】Rce漏洞数据不回显解决策略

一、数据带外1.1 DNSlog1.2 TCP-Portlog1.3 ICMP-Sizelog二、延迟判断三、写文件四、反弹权限4.1 反向连接4.2 正向连接免责声明 请勿使用本文中提到的技术进行非法测试或行为。使用本文中提供的信息或工具所造成的任何后果和损失由使用者自行承担,所产生的一切不良后果与文章…

Quantization

目录引线性量化 (Linear Quantization)对称量化非对称量化非线性量化Power-of-XRoundingDeterministic roundingStochastic rounding[1] 进击的程序猿-模型压缩-神经网络量化基础.[2] Przewlocka-Rus D., Sarwar S. S., Sumbul H. E., Li Y. and De Salvo B. Power-of-two quan…

H5-28 清除浮动

浮动元素会造成父元素高度塌陷后续元素也会受到影响 1、浮动的作用当元素设置fliat浮动后,该元素就会脱离文档流并向左/向右浮动①浮动元素会造成父元素高度塌陷②后续元素会受到影响 <div class="box"><div class="a"></div><div…

第58篇 Redis常用命令

1.基本操作2.字符串(Strings)3.列表()4.哈希(Hashes)5.位图(Bitmaps)6.位域(Bitfields)7.集合(Sets)8.有序集合(SortedSets)9.流(Streams)10。地理空间(Geospatial)11.HyperLogLog

位运算(未完成)

1、如果n & (n-1)=0,则n为2的幂 2、 题1:找出唯一成对的数 1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现? 性质:AA=0,B0=B …

seleniumwire获取请求头参数

selenium-wire扩展了 Selenium 的 Python 绑定,让您可以访问浏览器发出的底层请求。 您编写代码的方式与使用 Selenium 的方式相同,但您可以获得额外的 API 来检查请求和响应并动态更改它们 一:简介 selenium是爬虫常用的手段之一,由于是使用浏览器驱动模拟手动操作,所以只…

Confusion pg walkthrough Intermediate

namp ┌──(root㉿kali)-[~] └─# nmap -p- -A 192.168.188.99 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-04 04:50 UTC Nmap scan report for 192.168.188.99 Host is up (0.072s latency). Not shown: 65532 closed tcp ports (reset) PORT STATE SERVICE…

ESP32+micropython+作为WiFi热点与PC通信+TCP/IP接收并返回数据

ESP32+作为WiFi热点+接收并返回数据 ESP32代码# ESP32 作为AP # 作为服务器 接收数据 并echo# ESP32 作为AP 即wifi热点 import network import utime# 配置ESP32为AP模式 ssid = ESP32_AP password = 12345678 authmode = 4 # WPA2-PSK# 创建WLAN对象 wlan = network.W…