最近在项目中需要使用mybatis的批量插入功能,但是不知道为什么出现奇怪的错误,调试了很久,最终解决。
我的最初的mybatis批量插入语句如下:

1
2
3
4
5
6
<insert id="insertDfldBatch" useGeneratedKeys="true" keyProperty="id">
insert into dfld_new4 (way,sjdwz,ejdwz,stime,etime,sgcount,sgrate,distance,interval) values
<foreach item="item" collection="list" separator=",">
(#{item.way}, #{item.sjdwz}, #{item.ejdwz}, #{item.stime}, #{item.etime}, #{item.sgcount}, #{item.sgrate},#{item.distance},#{item.interval})
</foreach>
</insert>

Java中的调用如下:

1
2
3
4
List<SegProne> data = new ArrayList<>();	//保存多发路段
...
ScanDao sd = new ScanDao();
sd.saveDfldBatch(data);

1
2
3
4
5
6
7
8
9
10
//批量存储多发路段
public void saveDfldBatch(List<SegProne> data, String lm) {
SqlSession session = MyBatisUtil.getSqlSession();
ScanMapper sm = session.getMapper(ScanMapper.class);
if(data.size() > 0) { //有的道路没有多发路段
sm.insertDfldBatch(data);
}
session.commit();
session.close();
}

上述代码中data为需要存储的数据,由多个SegProne实体对象组成,通过调用saveDfldBatch方法保存。但是保存时出现如下错误:

网上搜索了一堆,最开始以为时插入的sql语句过长,于是删掉了interval,莫名奇妙居然好了,后来才发现原来interval是mysql中的关键字,sql语句中包含其关键字会报上述错误,好吧,居然是这个问题,当时完全没有考虑到。

当时还遇到的一个错误是mybatis一次性批量插入几万条记录可能会报错,因为mysql对语句的长度有限制,默认是 4M。遇到这种情况可以分批次进行插入,即将数据分成几个小批次,然后对每个小批次批量insert。
这里参考网上的封装一个对List数据分批的工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.vcc.utils;

import java.util.ArrayList;
import java.util.List;

public class ListGrouper{

/**
* 将集合按指定数量分组
* @param list 数据集合
* @param quantity 分组数量
* @return 分组结果
*/
public static <T> List<List<T>> groupListByQuantity(List<T> list, int quantity) {
if (list == null || list.size() == 0) {
return null;
}

if (quantity <= 0) {
new IllegalArgumentException("Wrong quantity.");
}

List<List<T>> wrapList = new ArrayList<List<T>>();
int count = 0;
while (count < list.size()) {
wrapList.add(new ArrayList<T>(list.subList(count, (count + quantity) > list.size() ? list.size() : count + quantity)));
count += quantity;
}

return wrapList;
}

public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 1; i <= 1011; i++) {
list.add(i);
}

List<List<Integer>> resultList = ListGrouper.groupListByQuantity(list, 50);
for (List<Integer> l : resultList) {
System.out.println(l);
}
System.out.println(resultList.size());
}

}

使用上述工具类对带插入数据分批处理,相应代码如下:

1
2
3
4
5
6
7
8
9
List<SegProne> data = new ArrayList<>();	//保存多发路段
...
ScanDao sd = new ScanDao();
//分批处理(mybatis批量插入长度有限制)
List<List<SegProne>> resultList = ListGrouper.groupListByQuantity(data, 1000);
System.out.println("分批: " + resultList.size());
for(List<SegProne> li: resultList) {
sd.saveDfldBatch(li);
}

上述以1000条记录为一个批次进行插入。

参考:
(1)https://my.oschina.net/zjllovecode/blog/1818716
(2)https://blog.csdn.net/hdg745979749/article/details/77921085