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


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

    背景:

    ​ 在使用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));
    
  • 相关阅读:
    sql 存储过程参数为空则不作为条件
    sql 将某一列转成字符串并且去掉最后一个逗号
    日期时间格式加减操作
    未能加载文件或程序集“NPOI”或它的某一个依赖项
    SqlBulkCopy 批量插入
    字符串操作
    CSS基本知识汇总
    ORACLE创建表之前判断表是否存在与SQL Server 对比使用
    SELECT INTO FROM 与 INSERT INTO SELECT区别鉴赏
    SQL 养成一个好习惯是一笔财富
  • 原文地址:https://www.cnblogs.com/qds1401744017/p/13563370.html
Copyright © 2020-2023  润新知