EasyExcel读取Excel数据(含多种方式)
目录
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进行读数据
引入依赖:
1 2 3 4 5 6 | <!-- https: //easyexcel.opensource.alibaba.com/docs/current/ --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version> 3.3 . 2 </version> </dependency> |
EasyExcel提供了两种读取模式
两种读取模式:
监听器:先创建监听器、在读取文件时绑定监听器。
单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。
同步读:无需创建监听器,一次性获取完整数据。
方便简单,但是数据量大时会有等待时常,也可能内存溢出。
使用 监听器 读取模式
1.创建一个实体类
并使用 EasyExcel 提供的注解 @ExcelProperty 与 Excel 表对应(注意:需要实现 get 、set 方法,不然对象可能获取不到数据)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @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.创建监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | // 有个很重要的点 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
1 2 3 4 | <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> |
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 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 方法,不然对象可能获取不到数据)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @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.代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面 */ @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)
以下只给出部分示例
创建监听器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | // 有个很重要的点 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( "存储数据库成功!" ); } } |
持久层:
1 2 3 4 5 6 7 8 | /** * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。 **/ public class DemoDAO { public void save(List<DemoData> list) { // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入 } } |
原文链接:https://blog.csdn.net/m0_59084856/article/details/135024825
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2021-04-12 关于docker一键部署后新建客户出错