MyBatis 实现批量添加、删除功能

  • 前情提要
  • 动态SQL(循环)
  • 代码准备
  • 批量添加
  • MySQL
  • Oracle
  • Oracle 批量插入(使用序列)
  • Oracle批量插入(不使用序列)
  • 批量删除


前情提要

对于数据量较大的业务来说,能批量操作简直不要太丝滑。数据导入、批量删除动态评论,确实能减少很多工作量。
要实现批量操作需要使用 MyBatis 的动态SQL。

动态SQL(循环)

<foreach collection="" item="" index="" open="" close="" separator=""></foreach>
属性解释:
	collection:遍历集合,
		1、入参是list、数组、map类型的可以直接写list、collection、map等
		2、或者按照参数的索引位置,arg0、arg1、param1、param2
		3、比较推荐第一种方式
	item:集合中的对象/泛型引用
	index:下标
	open:循环以某个字符开头
	close:循环以某个字符结尾
	separator:循环内容之间的分隔符,会自动去掉多余的分隔符

代码准备

实体类:

public class Student{
	private String no;
	private String name;
	private String sex;
	private String tel;
	private String address;
	private String clazz;
	private String delFlg

	// 省略 Set、get、toString 方法,可自行添加
}

持久层接口:

int add(List<Student> list);
int del(List<Student> list)

批量添加

MySQL

MySQL 批量插入相对简单,语法结构如下,通过分析所得,insert into tableName(c1, c2, c3, ...) values 是固定的,(v1, v2, v3, ...)是重复的,需要循环操作,也是循环的循环体,之间通过逗号隔开。

insert into tableName(c1, c2, c3, ...) values
(v1, v2, v3, ...),
(v1, v2, v3, ...),
(v1, v2, v3, ...),
(v1, v2, v3, ...),
....

基于以上分析,可得如下SQL:

<!--
	批量插入MySQL分析:
		insert into tb_student values
			('', '', '', '', ''),
			('', '', '', '', ''),
			('', '', '', '', ''),
			('', '', '', '', ''),
			('', '', '', '', '')
		循环体:
			('', '', '', '', '')
		分隔符:
			,
-->
<insert id="add">
	insert into tb_student
		(name, sex, tel, address, class, del_flg)
	values
		<foreach collection="list" item="stu" separator=",">
			(#{stu.name}, #{stu.sex}, #{stu.tel}, #{stu.address}, #{stu.clazz}, #{stu.delFlg})
		</foreach>
</insert>

Oracle

Oracle 的批量插入需求,使用表的拷贝语句,将一个结果集添加到表中。和单条数据的添加方式不同的是insert into tableName后面不再跟 value关键字,而是跟一个结果集。那么如何构建一个结果集呢?有两种方式:

1、基于数据库表查询到结果集。这个写法适用于表之间的数据拷贝。 2、基于查询固定值构建结果集。这个写法适用于没有表的形式。·

基于两种方式,形成如下两种语法结构:

insert into tableName(c1, c2, c3, ...)
(
    select s_c1, s_c2, s_c3, ... from subTableName
)


insert into tableName(c1, c2, c3, ...)
(
    select 数据1, 数据2, 数据3, ... from dual
    union all
    select 数据1, 数据2, 数据3, ... from dual
)

在实际的业务需求中,要添加的数据是从页面接收,传递到持久层的,所以不能使用表的方式构建结果集,自然就要使用查询固定值的方式构建结果集。另外,基于 Oracle 严谨的语法结构,查询时必须要保证语法的完整性,所以 from 后面要跟虚拟表 dual。

例如:select ‘张三’, 18, ‘男’, ‘17312341234’, … from dual。但是,一个查询语句只能表示一条数据,想要实现结果集需要将多个语句的结果进行合并。合并结果集的关键字是union all。

Oracle 批量插入(使用序列)

<!--
	批量插入Oracle(不带序列)分析:
		1、表的拷贝语句,从一张表中,把数据查询出来,添加到表中
			insert into tb_student (
				select * from tb_student_bk
			)
		2、【select * from tb_student_bk】查询出来的是结果集
			以上的需求就可以转换为将一个结果集直接添加到表中
			insert into tb_student (
				结果集
			)
		3、结果集的构成:
			3.1 查询具体的表
				select no, sex, tel from tb_student
			3.2 从虚拟表中查询固定的值(数据并不是只能存在表中,还可以固定写死)
				select '1', '男' from dual
				select '2', '女' from dual
				select '3', '男' from dual

			3.3 结果集的合并,union all
				select '1', '男' from dual
				union all
				select '2', '女' from dual
				union all
				select '3', '男' from dual

		4、综上
			insert into tb_student (
				select '1', '男' from dual
				union all
				select '2', '女' from dual
				union all
				select '3', '男' from dual
			)

			4.1 结果集的分析
				select #{no}, #{sex} from dual
				union all
				select #{no}, #{sex} from dual
				union all
				select #{no}, #{sex} from dual

				循环体:
					select #{no}, #{sex} from dual
				分隔符:
					union all
-->
<insert id="add">
	insert into tb_student
		(no, name, sex, tel, address, class, del_flg)
		(
			select seq_student.nextval, t.* from (
				<foreach collection="list" item="stu" separator="union all">
					select
						#{stu.name}, #{stu.sex}, #{stu.tel}, #{stu.address}, #{stu.clazz}, #{stu.delFlg}
					from
						dual
				</foreach>
			) t
		)
</insert>

Oracle批量插入(不使用序列)

<insert id="add">
	insert into tb_student
		(no, name, sex, tel, address, class, del_flg)
	(
		<foreach collection="list" item="stu" separator="union all">
			select
				#{stu.no}, #{stu.name}, #{stu.sex}, #{stu.tel}, #{stu.address}, #{stu.clazz}, #{stu.delFlg}
			from
				dual
		</foreach>
	)
</insert>

注意:Oracle 批量插入时要删除<setting name="useGeneratedKeys" value="true"/>,否则会报SQL 命令未正确结束错误。

批量删除

批量删除 MySQL 和 Oracle 类似,这里就直接上代码了。

<!--
	批量删除的SQL:逻辑删除修改删除的标识
		in中的学号#{no}出现了多次,多次出现同一个内容需要用到循环
		循环体是什么?
			#{no}
		循环体之间的分隔符?
			,
		for(Student stu : list)

	update tb_student set del_flg where no in (1,2,4,5,6,7,8)
-->
<update id="del">
	update tb_student
	set del_flg = '0'
	where no in
	<foreach collection="list" item="stu" separator="," open="(" close=")">
		#{stu.no}
	</foreach>
</update>