多线程导出mysql海量数据到excel表格

1.导入maven依赖包

   <!--引入easyexcel依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.3</version>
        </dependency>

2.导入工具类

package com.dk.config;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class ExcelHandler {

    private List<WriteSheet> sheets;
    private String dirName;
    private static final Integer DEFAULT_SHEETS_NUM = 5;
    private static final Integer DEFAULT_PER_SHEET_NUM = 1000000;

    public ExcelHandler(){}

    public ExcelHandler(String folderName){
        dirName = folderName;
    }

    /**
     *  创建excel和sheet,创建时可以指定sheet数量
     * @param excelName
     * @param clazz
     * @param sumSheet
     * @return
     */
    public ExcelWriter create(String excelName, Class clazz, int sumSheet){
        ExcelWriter excelWriter = EasyExcel.write(route(excelName), clazz.asSubclass(clazz)).build();
        createSheets(sumSheet);
        return excelWriter;
    }

    /**
     *  创建excel和sheet,sheet 数量默认 5, 最高可存放500W 行左右的数据,受每个sheet存放数据的限制
     * @param excelName
     * @param clazz
     * @return
     */
    public ExcelWriter create(String excelName, Class clazz){
        ExcelWriter excelWriter = EasyExcel.write(route(excelName), clazz.asSubclass(clazz)).build();
        createSheets(DEFAULT_SHEETS_NUM);
        return excelWriter;
    }

    /**
     *  写数据到excel, 仅使用一个sheet,不可用于百万以上数据
     * @param excelWriter
     * @param list
     */
    public void write(ExcelWriter excelWriter, List list){
        excelWriter.write(list, sheets.get(0));
    }

    /**
     *  写数据到excel
     * @param excelWriter
     * @param list    每一次的数据
     * @param sheetNum  sheet页码
     */
    public void write(ExcelWriter excelWriter, List list, int sheetNum){
        excelWriter.write(list, sheets.get(sheetNum));
    }

    /**
     *  写完数据关闭(finish 有关流操作),必须的操作
     * @param excelWriter
     */
    public void finish(ExcelWriter excelWriter){
        excelWriter.finish();
    }

    /**
     *  创建指定数量的sheet
     * @param num
     */
    private void createSheets(int num){
        sheets = new ArrayList();
        for (int i = 0; i <= num; i++) {
            WriteSheet sheet = EasyExcel.writerSheet(i, "sheet"+i).build();
            sheets.add(sheet);
        }
    }

    /**
     *  获取excel存放路径
     * @param excelName
     * @return
     */
    private String route(String excelName){
        if (null == dirName){
            dirName = "D:\\";
        }
        String filePath = "D:\\" + dirName + "/";
        File file = new File(filePath);
        if (!file.exists()){
            file.mkdirs();
        }
        return filePath + excelName + ".xlsx";
    }
}

3.对需要导出的实体类添加注解

package com.dk.entity;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * <p>
 * 
 * </p>
 *
 * @author qinyu
 * @since 2023-04-19
 */


@Data
@EqualsAndHashCode(callSuper = false)
@TableName("five_w")
@AllArgsConstructor
@NoArgsConstructor
@ContentRowHeight(15)
@HeadRowHeight(20)
@ColumnWidth(25)
public class W implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    @ExcelProperty("编号")
    private Long id;

    @TableField("name")
    @ExcelProperty("名字")
    private String name;

    @TableField("age")
    @ExcelProperty("年龄")
    private Integer age;

    @TableField("sex")
    @ExcelProperty("性别")
    private String sex;

    @TableField("phone")
    @ExcelProperty("电话")
    private String phone;

    @TableField("email")
    @ExcelProperty("邮箱")
    private String email;


}

4.自定义spring线程池ThreadPoolTaskExecutor 和 jdk 线程池 ThreadPoolExecutor

package com.dk.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
@EnableAsync
public class ThreadPoolExecutorConfig {


    @Bean("threadPoolExecutor")
    public ThreadPoolExecutor threadPoolExecutor(){
        return new ThreadPoolExecutor(16,32,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.AbortPolicy());
    }

    @Bean("springThreadPool")
    public ThreadPoolTaskExecutor springThreadPool(){

        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();

        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setMaxPoolSize(20);
        threadPoolTaskExecutor.setQueueCapacity(10);
        threadPoolTaskExecutor.setKeepAliveSeconds(20);
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;



    }

}

5.controller代码

@GetMapping("export")
public String  export(){
    wService.export();
    return "ok";
}

6.service层代码

 @Autowired
    private WMapper wMapper;

    @Autowired
    private ThreadPoolTaskExecutor springThreadPool;


    @Override
    public void export() {
        List<W> ws = wMapper.selectList(null);//查询出来全部需要导出的数据

        int pageSize = 20000;//每个excel存储的条数
        int totalPage = (ws.size() % pageSize) == 0 ? ws.size() / pageSize : (ws.size() / pageSize) + 1;//计算总页数
        for (int i = 1; i <= totalPage; i++) {//有几页就循环几次

            int pageNo = i;
            springThreadPool.execute(() -> {//使用线程池执行导出

                ExcelHandler handler = null;
                ExcelWriter writer = null;

                int start = pageNo <= 1 ? 0 : (pageNo - 1) * pageSize; //计算分页起始值
                //使用lambda表达式对总数据进行分页
                List<W> collect1 = ws.stream().skip(start).limit(pageSize).collect(Collectors.toList());
                try {
                    System.out.println(Thread.currentThread().getName() + "第" + pageNo + "次导入开始" + new Date());
                    // 创建handler对象 -- 参数文件夹名
                    handler = handler = new ExcelHandler("Thread"); //指定文件夹的名字
                    writer = writer = handler.create("记录" + pageNo, W.class);//指定表名 和实体类
                    handler.write(writer, collect1);
                    System.out.println("第" + pageNo + "次导入完毕" + new Date() + Thread.currentThread().getName());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (null != writer) {
                        handler.finish(writer);
                    }
                }


            });
        }
    }

7.执行结果 测试5w 数据仅需2s

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值