首页 > 基础资料 博客日记

Java 使用 EasyExcel 实现百万级数据的秒级导出

2025-01-01 15:00:07基础资料围观47

文章Java 使用 EasyExcel 实现百万级数据的秒级导出分享给大家,欢迎收藏Java资料网,专注分享技术知识

在数据处理领域,导出大数据量的 Excel 文件是一个常见的需求,尤其是在企业级应用中。无论是报表导出、数据分析,还是系统日志生成,Excel 文件作为一种通用的数据交换格式,广泛应用于各行各业。然而,随着数据量的不断增大,传统的导出方式往往会面临性能瓶颈,导致内存溢出或导出时间过长。如何高效处理百万级数据的导出,成为了一个需要解决的问题。

在 Java 中,EasyExcel 是一个高效、简便的 Excel 操作库,相比传统的 Apache POI,EasyExcel 通过减少内存占用和优化性能,提供了更适合大数据量处理的解决方案。本文将介绍如何使用 EasyExcel 高效导出百万级数据。

为什么选择 EasyExcel

在处理大数据量的 Excel 导出时,选择合适的工具库至关重要。与传统的 Apache POI 相比,EasyExcel 具有以下几个优点:

  1. 内存占用低:EasyExcel 使用了流式 API,避免了将整个 Excel 文件加载到内存中,这样可以大大降低内存的消耗。
  2. 高效性能:通过减少 Java 对象和 Excel 的频繁转换,EasyExcel 提供了更高效的性能,特别适合处理大量数据。
  3. 简单易用:EasyExcel 提供了简洁的 API,能够快速实现各种常见的 Excel 操作,降低了开发的复杂度。

环境准备

在开始之前,你需要先将 EasyExcel 添加到项目中。可以使用 Maven 或 Gradle 来引入 EasyExcel。

Maven 依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.0</version> <!-- 请根据需要选择最新版本 -->
</dependency>

主要代码逻辑 :

                dto:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value="excel_table",autoResultMap = true)
public class ExcelTable {

    @TableId(type = IdType.AUTO)
    private int id;

    @ExcelProperty(index = 1)  // 'name' 的索引为 1
    private String name;

    @ExcelProperty(index = 2)  // 'sex' 的索引为 2
    private String sex;

    @ExcelProperty(index = 3)  // 'age' 的索引为 3
    private Integer age;

    @ExcelProperty(index = 4)  // 'address' 的索引为 4
    private String address;

    @ExcelProperty(index = 5)  // 'email' 的索引为 5
    private String email;

    @ExcelProperty(index = 6)  // 'phone' 的索引为 6
    private String phone;
}

        mapper:

@Mapper
public interface ExcelTableMapper extends BaseMapper<ExcelTable> {

    /**
     * 批量插入 ExcelTable 数据
     * @param excelTables ExcelTable 对象列表
     * @return 插入成功的记录数
     */
    @Insert({
            "<script>",
            "INSERT INTO excel_table (name, sex, age, address, email, phone) VALUES",
            "<foreach collection='list' item='item' separator=','>",
            "(#{item.name}, #{item.sex}, #{item.age}, #{item.address}, #{item.email}, #{item.phone})",
            "</foreach>",
            "</script>"
    })
    int batchInsert(List<ExcelTable> excelTables);


}

        监听器:

@Slf4j
@Service
public class ExcelTableListener extends AnalysisEventListener<ExcelTable> {

    /*单次处理条数*/
    private final static int BATCH_COUNT = 1000000;
    // 创建线程池
    ExecutorService executorService = Executors.newFixedThreadPool(8); // 创建 4 个线程进行并行处理

    private final List successList = new ArrayList<ExcelTable>();

    // 批量存储数据的容器(例如,你可以使用一个 List)
    private ExcelTableMapper excelTableMapper;

    public ExcelTableListener(ExcelTableMapper excelTableMapper) {
        this.excelTableMapper = excelTableMapper;
    }

    // 每读取一行数据,就会调用这个方法
    @Override
    public void invoke(ExcelTable excelTable, AnalysisContext context) {
        successList.add(excelTable);
        if(successList.size() >= BATCH_COUNT){
            log.info("读取数据:{}", successList.size());
            saveData(successList);
            successList.clear();
        }
    }

    // 读取完成后的处理方法(在 Excel 文件全部读取完之后调用)
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 你可以在这里执行一些收尾操作,通常用于关闭流或者进行批量保存等操作
        saveData(successList);
        successList.clear();
    }


    /**
     * 采用多线程读取数据
     */
    private void saveData(List<ExcelTable> batchList) {
        //每两千条数据,进行一次批量插入
        log.info("-----开始进行数据的入库");
        List<List<ExcelTable>> partition = ListUtils.partition(batchList, 100000);
        CountDownLatch countDownLatch = new CountDownLatch(partition.size());
        for (List<ExcelTable> excelTables : partition) {
            executorService.execute(()->{
                log.info("-------进行数据的插入");
                excelTableMapper.batchInsert(excelTables);
                countDownLatch.countDown();
            });
        }
        // 等待所有线程执行完
        try {
            countDownLatch.await();
        } catch (Exception e) {
            log.error("等待所有线程执行完异常,e:{}", e.getMessage(), e);
        }
        log.info("-------入库结束");
    }
}

        测试方法:

    @Autowired
    private ExcelTableMapper excelTableMapper;
    @Test
    public void excelToDb(){
        String fileName = "D:\\home\\aaaac.XLSX";
        //记录开始读取Excel时间,也是导入程序开始时间
        long startReadTime = System.currentTimeMillis();
        System.out.println("------开始时间:" + startReadTime + "ms------");
        //读取所有Sheet的数据.每次读完一个Sheet就会调用这个方法
       // EasyExcel.read(fileName, new ExcelTableListener(excelTableMapper)).doReadAll();
        EasyExcel.read(fileName, ExcelTable.class, new ExcelTableListener(excelTableMapper))
                .doReadAll();
        long endReadTime = System.currentTimeMillis();
        System.out.println("------结束时间:" + endReadTime + "ms------");
        System.out.println("------导出数据总用时:"+(endReadTime-startReadTime)/1000);

    }

运行结果:总用时154秒 

        我们导出的数据量是:四百多万条

 

总结

使用 EasyExcel 导出百万级数据时,关键在于如何优化内存使用和性能。通过以下几种方式,我们可以有效地解决大数据量导出的挑战:

  1. 使用流式写入:避免一次性将所有数据加载到内存中,减少内存占用。
  2. 分批写入:将数据分批处理,每次写入小部分数据,避免内存溢出。
  3. 多线程优化:在导出过程中使用多线程并行处理数据,提高导出速度。

EasyExcel 提供了高效且易于使用的 API,使得大数据量的 Excel 导出变得简单且高效。在实际开发中,结合这些技术,可以轻松应对百万级数据的导出任务。

点点关注,点点赞呀,持续更新有用的知识............

 

 

 

 


文章来源:https://blog.csdn.net/w_l666/article/details/144261380
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云