EasyExcel读取Excel数据(含多种方式)

news2024/12/27 13:33:13

目录

EasyExcel简介

使用EasyExcel进行读数据

引入依赖:

EasyExcel提供了两种读取模式

使用 监听器 读取模式

1.创建一个实体类

2.创建监听器

代码

使用 同步读 读取模式

 1.创建一个实体类

2.代码

添加导入数据库的逻辑


其实官方文档讲得很清楚,可以看官方文档
官网:关于Easyexcel | Easy Excel (alibaba.com)

EasyExcel简介

简单地说,EasyExce是一个Java库,用于快速、简单地读写Excel文件 

以下是官网的简介:
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便

使用EasyExcel进行读数据

引入依赖:
<!-- https://easyexcel.opensource.alibaba.com/docs/current/ -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.3.2</version>
        </dependency>

EasyExcel提供了两种读取模式

两种读取模式:

  1. 监听器:先创建监听器、在读取文件时绑定监听器。

单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。

  1. 同步读:无需创建监听器,一次性获取完整数据。

方便简单,但是数据量大时会有等待时常,也可能内存溢出。

使用 监听器 读取模式
1.创建一个实体类

并使用 EasyExcel 提供的注解 @ExcelProperty 与 Excel 表对应(注意:需要实现 get 、set 方法,不然对象可能获取不到数据)

@Data
public class DemoData {
    /**
     * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
     */
    @ExcelProperty(index= 0)
    private Long planetCode;
    /**
     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
     */
    @ExcelProperty(value = "成员昵称")
    private String username;
    @ExcelProperty(value = "本月积分")
    private Double integral;
}

这里使用到了lombok中的@Data注解

@Data注解的类,编译后会自动给我们加上下列方法:

  • 所有属性的get和set方法
  • toString 方法
  • hashCode方法
  • equals方法
2.创建监听器
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {

    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);


    public DemoDataListener() {

    }



    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        log.info("解析到一条数据:{}", new Gson().toJson(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {

    }
}

 这里要引入gson

<dependency>
   <groupId>com.google.code.gson</groupId>
   <artifactId>gson</artifactId>
</dependency>

代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.util.ListUtils;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;


import java.util.List;

@Slf4j
public class ImportExcel {
    /**
     * 最简单的读
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
     * <p>
     * 3. 直接读即可
     */
    @Test
    public void simpleRead() {
        log.info("==========================写法1 不需要创建监听器===========================");
        // 写法1:JDK8+ ,不用额外写一个DemoDataListener
        // since: 3.0.0-beta1
        //3.0.0版本之后,使用这种方法不需要创建监听器
        String fileName = "D:\\testExcel.xlsx";
        // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
        // 具体需要返回多少行可以在`PageReadListener`的构造函数设置
        EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
            for (DemoData demoData : dataList) {
                log.info("读取到一条数据{}", new Gson().toJson(demoData));
            }
        })).sheet().doRead();


        log.info("==========================写法2 匿名内部类===========================");
        // 写法2:
        // 匿名内部类(创建一个监听器对象) 不用额外写一个DemoDataListener
        fileName = "D:\\testExcel.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, DemoData.class, new ReadListener<DemoData>() {
            /**
             * 单次缓存的数据量
             */
            public static final int BATCH_COUNT = 100;
            /**
             *临时存储
             */
            private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(DemoData data, AnalysisContext context) {
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    // 存储完成清理 list
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }

            /**
             * 加上存储数据库
             */
            private void saveData() {
                log.info("{}条数据,开始存储数据库!", cachedDataList.size());
                log.info("存储数据库成功!");
            }
        }).sheet().doRead();


        log.info("==========================写法3 需创建监听器===========================");
        // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
        // 写法3:
        //这种方法需要写一个监听器
        fileName = "D:\\testExcel.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();


        log.info("==========================写法4 读单文件多个sheet===========================");
        // 写法4
        //这种方法可以读一个文件里的多个sheet
        fileName = "D:\\testExcel.xlsx";
        // 一个文件一个reader
        try (ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build()) {
            // 构建一个sheet 这里可以指定名字或者no
            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            // 读取一个sheet
            excelReader.read(readSheet);
        }


        
    }
}

使用 同步读 读取模式
 1.创建一个实体类

并使用 EasyExcel 提供的注解 @ExcelProperty 与 Excel 表对应(注意:需要实现 get 、set 方法,不然对象可能获取不到数据)

@Data
public class DemoData {
    /**
     * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
     */
    @ExcelProperty(index= 0)
    private Long planetCode;
    /**
     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
     */
    @ExcelProperty(value = "成员昵称")
    private String username;
    @ExcelProperty(value = "本月积分")
    private Double integral;
}
2.代码
/**    
     * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面
     */
    @Test
    public void synchronousRead() {
        String fileName = "D:\\testExcel.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
        List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
        for (DemoData data : list) {
            log.info("读取到数据:{}", new Gson().toJson(data));
        }
        // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish
        List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
        for (Map<Integer, String> data : listMap) {
            // 返回每条数据的键值对 表示所在的列 和所在列的值
            log.info("读取到数据:{}",  new Gson().toJson(data));
        }
    }
添加导入数据库的逻辑

如果需要将导入的数据添加到数据库,看官网读Excel | Easy Excel (alibaba.com)

以下只给出部分示例

创建监听器:

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {

    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private DemoDAO demoDAO;

    public DemoDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        log.info("解析到一条数据:{}", JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", cachedDataList.size());
        demoDAO.save(cachedDataList);
        log.info("存储数据库成功!");
    }
}

 持久层:
 

/**
 * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
 **/
public class DemoDAO {
    public void save(List<DemoData> list) {
        // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1314756.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

git 的使用

git reset详解-CSDN博客 git reset 命令详解 git revert命令详解。-CSDN博客 关于Git分支中HEAD和Master的理解 - 知乎 (zhihu.com) 一文带你精通 Git&#xff08;Git 安装与使用、Git 命令精讲、项目的推送与克隆&#xff09;-CSDN博客 Git 常用操作&#xff08;5&#xff…

【Linux】tree命令使用

tree命令 tree命令用于以树状图列出目录的内容。 语法 tree [参数] [目录] tree 命令 -Linux手册页 bash: tree: 未找到命令... 安装tree yum -y install tree如果你系统中有安装tree 但是还是执行找不到该命令的话&#xff0c;那原因就是&#xff1a;环境变量错误&#x…

智能优化算法应用:基于热交换算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于热交换算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于热交换算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.热交换算法4.实验参数设定5.算法结果6.参考文…

【数字电路】MacBook使用iverilog进行数字电路仿真

安装流程 在终端中用brew包管理工具进行安装仿真工具&#xff1a; 编译verilog代码&#xff1a; brew install icarus-verilog编译verilog代码&#xff1a; brew install verilatorMacOS系统显示UNIX GUI brew install xquartz可视化仿真波形图&#xff1a; brew install gtk…

2024年天津体育学院专升本专业课网上报名确认缴费安排

天津体育学院2024年高职升本科专业考试报名安排 一、时间安排 1.报名时间&#xff1a;2023年12月19日9&#xff1a;00-12月21日17&#xff1a;00 2.缴费时间&#xff1a;2023年12月26日-27日 &#xff08;考试考务费&#xff1a;体育教育专业&#xff1a;160元/人&#xff…

项目中使用Arrays.asList、ArrayList.subList的坑

使用Arrays.asList的注意事项 1.1 可能会踩的坑 先来看下Arrays.asList的使用&#xff1a; List<Integer> statusList Arrays.asList(1, 2); System.out.println(statusList); System.out.println(statusList.contains(1)); System.out.println(statusList.contains(3)…

C之switch小问题

执行结果&#xff1a; 为什么会是100呢&#xff1f; 因为C语言会忽视 switch语句与第一个case之间的code&#xff0c;也就是根本不会执行 “num100;

el-date-picker 选择一个或多个日期

el-date-picker可选择多个日期 type“dates” 加个s即可 <div><span>el-date-picker选择多个日期</span><el-date-pickertype"dates"v-model"dateList"placeholder"选择一个或多个日期"></el-date-picker></di…

RabbitMQ插件详解:rabbitmq_message_timestamp【Rabbitmq 五】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 RabbitMQ时空之旅&#xff1a;rabbitmq_message_timestamp的奇妙世界 前言什么是rabbitmq_message_timestamprabbitmq_message_timestamp 的定义与作用&#xff1a;如何在 RabbitMQ 中启用消息时间戳&…

聚观早报 |红魔9 Pro氘锋透明银翼版开售;荣耀90 GT将发布

【聚观365】12月16日消息 红魔9 Pro氘锋透明银翼版开售 荣耀90 GT将发布 德国成功化解预算僵局 第二届起点读书“网文填坑节”收官 阿维塔科技拟 2025 年赴港上市 红魔9 Pro氘锋透明银翼版开售 现在有最新消息&#xff0c;近日红魔9 Pro氘锋透明银翼版本开启全款预售&…

Java实现插入排序及其动图演示

插入排序是一种简单直观的排序算法。它的基本思想是将一个待排序的元素插入到已经排序好的序列中的适当位置&#xff0c;从而得到一个新的、元素个数加一的有序序列。 具体的插入排序算法过程如下&#xff1a; 从第一个元素开始&#xff0c;认为第一个元素已经是有序序列。取…

C/C++ STL提供的序列式容器之deque

deque是双向开口的连续内存空间&#xff08;动态将多个连续空间通过指针数组接合在一起&#xff09;&#xff0c;随时可以增加一段新的空间。 deque 的最大任务就是在这些分段的连续空间上&#xff0c;维护其整体连续的假象&#xff0c;并提供随机存取的接口。 特点 1. 一…

风速预测(五)基于Pytorch的EMD-CNN-LSTM模型

目录 前言 1 风速数据EMD分解与可视化 1.1 导入数据 1.2 EMD分解 2 数据集制作与预处理 2.1 先划分数据集&#xff0c;按照8&#xff1a;2划分训练集和测试集 2.2 设置滑动窗口大小为96&#xff0c;制作数据集 3 基于Pytorch的EMD-CNN-LSTM模型预测 3.1 数据加载&…

KMP算法, 什么是KMP算法 ,暴力匹配 ,KMP算法实现

文章目录 KMP算法什么是KMP算法暴力匹配KMP算法实现 KMP算法 什么是KMP算法 KMP是Knuth、Morris和Pratt首字母的缩写&#xff0c;KMP也是由这三位学者发明&#xff08;1977年联合发表论文&#xff09;。 KMP主要应用在字符串的匹配&#xff0c;是一个解决模式串在文本串是否…

OxLint 发布了,Eslint 何去何从?

由于最近的rust在前端领域的崛起&#xff0c;基于rust的前端生态链遭到rust底层重构&#xff0c;最近又爆出OxLint&#xff0c;是一款基于Rust的linter工具Oxlint在国外前端圈引起热烈讨论&#xff0c;很多大佬给出了高度评价&#xff1b;你或许不知道OxLint&#xff0c;相比ES…

互联网大厂月薪分布:字节跳动超 5% 员工月薪高于 5 万

近期&#xff0c;某统计机构公开了国内互联网巨头的薪资分布情况。根据统计数据显示&#xff0c;贝壳、阿里、滴滴、拼多多、快手和腾讯等公司超过60%的员工&#xff0c;月薪集中在3-5万的区间。而拼多多和字节跳动更有超过5%的员工月薪超过5万。 华为&#xff0c;一个众所周知…

error: src refspec master does not match any

新项目 push 至 github 仓库的时候抛出了如下异常 error: src refspec master does not match any 解决办法 首先,查看当前 branch, 因新项目只有一个 main git branch早期都是 master 而不是 main,所以将现有的改成 main 或者 master 均可 git branch -m main // 或者 git…

前端自定义验证码,校验验证码,验证码时效

最近做的项目&#xff0c;不需要后端接口&#xff0c;只需要前端验证&#xff0c;如图 初始页面 获取验证码 验证码的文件&#xff0c;直接复制就行 <template><div class"s-canvas"><canvasid"s-canvas":width"contentWidth":…

【k8s】使用Finalizers控制k8s资源删除

文章目录 词汇表基本删除操作Finalizers是什么&#xff1f;Owner References又是什么&#xff1f;强制删除命名空间参考 你有没有在使用k8s过程中遇到过这种情况: 通过kubectl delete指令删除一些资源时&#xff0c;一直处于Terminating状态。 这是为什么呢&#xff1f; 本文将…

Docker-consule 服务发现与注册

consul服务更新和服务发现 什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。直到后来出现了多个节点的分布式架构&…