zoukankan      html  css  js  c++  java
  • 工具类-如何实现一个分段插入数据的工具类?

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

    背景:

    ​ 在使用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));
    
  • 相关阅读:
    Java并发(三):重排序
    Java并发(六):volatile的实现原理
    Java并发(五):synchronized实现原理
    Java并发(一):多线程干货总结
    JDK源码学习笔记——String
    Java并发(二):Java内存模型
    JVM命令-java服务器故障排查
    vue之数据请求方式
    vue之菜单添加选择,知识:数据双向绑定、循环渲染、事件点击以及按键的点击
    Vue【第3章】:Vue常用指令二:事件和方法
  • 原文地址:https://www.cnblogs.com/qds1401744017/p/13563370.html
Copyright © 2011-2022 走看看