工具类-如何实现一个分段插入数据的工具类?
背景:
在使用mybatis做大量数据(万级以上)插入数据库时( 例如全表更新时) 不可避免地会遇到超限异常,例如:
超过mysql
的max_allowed_packet
配置限制导致的异常` com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large
当然你可以通过调大设置来解燃眉之急, 但如果数据了真的够大, 分段插入就治标治本了
解决方案:
思路: 我们要做工具类实现分段插入, 对用户影响最小化, 那就让用户只需要把要插入的数据集, 和要做插入的插入 语句(或者mapper接口)传给我们的工具类. 数据集好传啊, 方法怎么传呢?
$\color{red}{方案1}$: 使用抽象类的抽象方法, 让用户实现抽象类中的抽象方法来传递方法, 然后我们在工具类中可以调用这个抽 象方法当作用户的方法来使用:
/*
* @description: 自己实现插入方法
* @Author qiao.dashan
* @Date 2020/8/25 15:11
*/
protected abstract <T> int saveFunc(List<T> list);
public <T> int doSave(List<T> list,int bulkNum){
if(bulkNum==0){
bulkNum = 3;
}
int inflowRows = 0;
if(list.size() <= bulkNum){
inflowRows = saveFunc(list);
}else{
int times = list.size() / bulkNum;
for (int i = 0; i < times ; i++) {
inflowRows += saveFunc(list.subList(i * bulkNum, (i+1) * bulkNum));
}
inflowRows += saveFunc(list.subList(times * bulkNum, list.size()));
}
return inflowRows;
}
测试代码如下, 经测试有效:
ArrayList list = new ArrayList<PhotoInfo>();
for (int i = 0; i < 5; i++) {
PhotoInfo pn = new PhotoInfo();
pn.setPhotoId("" + i);
pn.setPhotoUrl("u"+i);
pn.setPhotoName("n" + i);
pn.setSourceId("i" + i);
pn.setSourceType("t" + i);
list.add(pn);
}
//抽象方法的方式实现(将插入方法作为抽象方法的实现传入工具类)
int i = new BulkUpdateUtil() {
@Override
protected int saveFunc(List list) {
return photoMapper.savePhoto(list);
}
}.doSave(list, 2);
return i;
$\color{red}{方案2}$: 使用lambda表达式, 定义一个函数式接口做形参, 将插入方法传入工具类
/*
函数式接口作为形参接收插入方法
*/
@FunctionalInterface
public interface ISave<T> {
int excuteSave(List<T> list);
}
/*
* @description: 执行分段插入
* @Author qiao.dashan
* @Date 2020/8/25 14:50
*/
public static <T> int doSave(List<T> list,int bulkNum,ISave<T> save){
if(bulkNum==0){
bulkNum = 3;
}
int inflowRows = 0;
if(list.size() <= bulkNum){
inflowRows = save.excuteSave(list);
}else{
int times = list.size() / bulkNum;
for (int i = 0; i < times ; i++) {
inflowRows += save.excuteSave(list.subList(i * bulkNum, (i+1) * bulkNum));
}
inflowRows += save.excuteSave(list.subList(times * bulkNum, list.size()));
}
return inflowRows;
}
测试结果有效:
ArrayList list = new ArrayList<PhotoInfo>();
for (int i = 0; i < 50000; i++) {
PhotoInfo pn = new PhotoInfo();
pn.setPhotoId("" + i);
pn.setPhotoUrl("u"+i);
pn.setPhotoName("n" + i);
pn.setSourceId("i" + i);
pn.setSourceType("t" + i);
list.add(pn);
}
return BulkUpdateUtil.doSave(list, 2, (List<PhotoInfo> list1) -> photoMapper.savePhoto(list1));