本文小编为大家详细介绍“MyBatis如何实现批量插入数据”,内容详细,步骤清晰,细节处理妥当,希望这篇“MyBatis如何实现批量插入数据”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
一、SQL实现示例
假设我们只插入一条数据的时候,SQL如下
insert into table ([列名],[列名]) values ([列值],[列值]));
# 或者
insert into table values ([列值],[列值]))
当插入多条数据的时候,也就是需要批量插入的时候,SQL如下
insert into table ([列名],[列名])
VALUES
([列值],[列值])),
([列值],[列值])),
([列值],[列值]));
批量的用处:一次插入多条数据,这样就可以降低与数据库的IO次数,减少性能的损耗。
二、Mybatis通过Mapper.xml文件实现
比如这里的抽象的SQL就是
insert into table (ID, PHONE,MESSAGE,APP_CODE,AREA_CODE,SEND_TYPE,SEND_TIME,CREATE_DATE,REMARK,serial_number)
values
(?,?,?,?,?,?,?,?,?,?,?,), (?,?,?,?,?,?,?,?,?,?,?,), (?,?,?,?,?,?,?,?,?,?,?,)
具体实现
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.scoreone.ap.mapper.MtTaskMapper">
<!--批量插入-->
<insert id="insertList" parameterType="java.util.List">
insert into ap_mt_task (ID, PHONE, MESSAGE,
APP_CODE, AREA_CODE, SEND_TYPE,
SEND_TIME, CREATE_DATE, REMARK,serial_number
)
VALUES
<foreach collection="list" index="index" item="item" separator="," >
(#{item.ID},#{item.PHONE},#{item.MESSAGE},#{item.APP_CODE},#{item.AREA_CODE},
#{item.SEND_TYPE},#{item.SEND_TIME},#{item.CREATE_DATE},#{item.REMARK},#{item.serial_number})
</foreach>
</insert>
</mapper>
也可以不用在value里用
foreach
,在整个SQL语句外面用。参数解释
foreach的主要作用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有 collection,item,separator,index,open,close。
collection
:指定要遍历的集合。表示传入过来的参数的数据类型。该属性是必须指定的,要做 foreach 的对象。在使用foreach的时候最关键的也是最容易出错的就是collection属性。在不同情况 下,该属性的值是不一样的,主要有一下3种情况:a. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
b. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
c. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map。Map 对象没有默认的键
item
:表示集合中每一个元素进行迭代时的别名。将当前遍历出的元素赋值给指定的变量,然后用#{变量名},就能取出变量的值,也就是当前遍历出的元素。
separator
:表示在每次进行迭代之间以什么符号作为分隔符。select * from tab where id in(1,2,3)相当于1,2,3之间的","
index
:索引。index指定一个名字,用于表示在迭代过程中,每次迭代到的位置。遍历list的时候index就是索引,遍历map的时候index表示的就是map的key,item就是map的值。
open
:表示该语句以什么开始,close表示以什么结束。三、在Mapper接口上使用注解
举例
@Insert(“insert into blog(blogId,title,author) values(#blogId,#title,#author)”)
public boolean saveBlog(Blog blog);
这种对于简单的SQL可以使用法,对于复杂的SQL可读性太差(会有以大堆拼接符号),不建议使用。
xml、注解两种方式的区别:
foreach相当语句逐条INSERT语句执行,将出现如下问题:
(1)mapper接口的insert方法返回值将是最后一条INSERT语句的操作成功的记录数目(就是0或1),而不是所有INSERT语句的操作成功的总记录数目;
(2)当其中一条不成功时,不会进行整体回滚。
注解方式:当有一条插入不成功时,会整体回滚。
四、限制一次批量插入数据的数量
假设我们传过来的List一次有 10000 条,但是我们数据库批量插入不可能一下子插入 10000 条,我们就需要将这一批的数据进行限制,也就是再分成一小批一小批进行插入,比如下面,一次限制插入 15 条数据。
//举例这里就是进行 task表 的批量插入,限制一次 15 条
public int insertList(ArrayList<MtTask> taskList) {
//批量插入的时候,限制一批插入 15 条,效率会高一点,防止造成堆内存溢出异常
if (taskList.size() > 0) {
int InsertSize = 15;//一次插入15条
//分成limit次发请求到数据库
//这个算法就是防止不是在整除的时候少了一次,比如 32 个需要插四次
int limit = (taskList.size() + InsertSize - 1) / InsertSize;//需要插入多少次
//这个流的意思就是,进行 limit 次,每一次插入15个
//根据起始值seed(0),每次生成一个指定递增值(n+1)的数,limit(limit)用于截断流的长度,也就是进行limit次的里面的操作
Stream.iterate(0, n -> n + 1).limit(limit).forEach(a -> {
// skip就是跳过前面(a * InsertSize)条数据,因为 a是从0开始,到limit,skip(0)的时候是空的不插入
// .limit(InsertSize)->限制每次插入数据的15条 .collect(Collectors.toList()->组成一个toList
List<MtTask> mtTaskList = taskList.stream().skip(a * InsertSize).limit(InsertSize).collect(Collectors.toList());
//doSomething();
mtTaskMapper.insertList(mtTaskList);
});
}
上面这是使用流的,下面是不用使用流的
/*不用流的方法*/
//集合的大小
int size = taskList.size();
//需要拆分成的每个集合大小
int newSize = 15;
//需要需要拆分成的小集合数量(拆分没有余数则取相除的结果,如果有余数则需要再加一个集合存放余数)
//这个就是直接判断出了之后有没有余数,有就多加一次,没有就整除刚好
int sum = size % newSize != 0 ? (size / newSize) + 1 : size / newSize;
//循环参照大集合,能拆分成的集合有多少个就循环多少遍
for (int i = 0; i < sum; i++) {
//如果当前下标+1 等于拆分成的集合数量,则说明这是最后一组(也可能仅能拆成一个集合 下标0 + 1 == 集合数量 1)
if ((i + 1) == sum) {
//截取的下标开始位
int startIndex = (i * newSize);
//截取的结束下标位置
int endIndex = size;
//插入
result = mtTaskMapper.insertList(taskList.subList(startIndex, endIndex));
} else {
//截取的下标开始位
int startIndex = (i * newSize);
//截取的结束下标位置
int endIndex = (i + 1) * newSize;
result = mtTaskMapper.insertList(taskList.subList(startIndex, endIndex));
}
}
=还有一种就是达到我们限制的数量就插入
@Override
public int insertList(ArrayList<MtTask> taskList) {
if(!CollectionUtils.isEmpty(taskList)){
List<MtTask> subList = new ArrayList<>();
int num = 0;
for (MtTask mtTask : taskList ){
subList.add(mtTask);
num ++ ;
if(num >= 500){
mtTaskMapper.insertList(subList);
subList.clear();
subList = new ArrayList<>();
}
}
if(num > 0){
mtTaskMapper.insertList(subList);
subList.clear();
}
}
}