工具类-如何实现一个分段插入数据的工具类?

背景:

​ 在使用mybatis做大量数据(万级以上)插入数据库时( 例如全表更新时) 不可避免地会遇到超限异常,例如:

​ 超过mysqlmax_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));
posted on 2020-08-26 09:50  一贯可乐  阅读(189)  评论(0编辑  收藏  举报



123