一、前言
今天小编带大家一起整合一下easyExcel,之所以用这个,是因为easyExcel性能比较好,不会报OOM!
市面上常见的导入导出Excel分为三种:
hutooleasyExcelpoihutool和easyExcel都是对poi的封装,使用起来更加方便!
二、导入依赖
小编这里是3.0.X版本的,版本不同可能导致部分有出入,如果大家版本是3.1.X,可以去官方文档看看有不一样的!
官方文档:https://easyexcel.opensource.alibaba.com/
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency>
三、实体类
这里可以自带的转换器:
@DateTimeFormat(“yyyy年MM月dd日HH时mm分ss秒”)LocalDateTimeStringConverter或者自定义转化器:实现:implements Converter<T>。
具体文档:官方文档:https://easyexcel.opensource.alibaba.com/docs/3.0.x/quickstart/read#%E6%97%A5%E6%9C%9F%E6%95%B0%E5%AD%97%E6%88%96%E8%80%85%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%BC%E5%BC%8F%E8%BD%AC%E6%8D%A2
@ExcelProperty参数注意:
这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配。
用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据。
/*** @author wangzhenjun* @date 2022/12/2 15:52*/@DatapublicclassTest {@TableIdprivateIntegerid;@ExcelProperty(index=0)privateStringname;@ExcelProperty(index=1)privateIntegerage;@ExcelProperty(index=2,converter=LocalDateTimeStringConverter.class)privateLocalDateTimetime;}
四、编写监听器
注意点:这个监听器一定不要是单例的,被spring管理默认为单例,如果要使用@Component,一定要加上:@Scope(“prototype”),这样在创建完后spring不会进行管理,每次都会是新bean!不加@Component在导入时要进行new ImportDataListener!小编这里不想new了直接这样写!!如果不想这样,可以使用构造器set进行使用!BATCH_COUNT:数据阈值,超过了就会清理list,在之前可以进行保存到数据库中,方便内存回收,防治OOM!这里保存到数据库中一般使用批量保存,不要解析到一行就去保存数据库中,这样数据量大会给数据库增加IO,导致挂掉!这里小编使用ServiceImpl的saveBatch()方法,也可以自己写一下,像小编这样写,会出现循环依赖,加上@Lazy就行!
/*** @author wangzhenjun* @date 2022/12/2 15:38*/@Slf4j@Component// 每次bean都是新的,不要单例@Scope("prototype")publicclassImportDataListenerimplementsReadListener<Test> {@Autowired@LazyprivateTestServicetestService;/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/privatestaticfinalintBATCH_COUNT=100;/*** 缓存的数据*/privateList<Test>importExcelDataList=ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);/*** 这个每一条数据解析都会来调用** @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublicvoidinvoke(Testdata, AnalysisContextcontext) {log.info("解析到一条数据:{}", JSON.toJSONString(data));importExcelDataList.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (importExcelDataList.size() >=BATCH_COUNT) {saveData();// 存储完成清理 listimportExcelDataList=ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); } }/*** 所有数据解析完成了 都会来调用** @param context*/@OverridepublicvoiddoAfterAllAnalysed(AnalysisContextcontext) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();log.info("所有数据解析完成!"); }/*** 加上存储数据库*/privatevoidsaveData() {log.info("{}条数据,开始存储数据库!", importExcelDataList.size());testService.saveBatch(importExcelDataList);log.info("存储数据库成功!"); }}
五、Controller
/*** @author wangzhenjun* @date 2022/10/26 16:51*/@Slf4j@RestController@RequestMapping("/test")publicclassTestController {@AutowiredprivateTestServicetestService;@PostMapping("/import")publicResultimportExcel(@RequestBodyMultipartFilemultipartFile){testService.importExcel(multipartFile);returnResult.success("ok"); }}
六、Service
/*** @author wangzhenjun* @date 2022/10/26 16:55*/publicinterfaceTestServiceextendsIService<Test> {voidimportExcel(MultipartFilemultipartFile);}
七、ServiceImpl
/*** @author wangzhenjun* @date 2022/10/26 16:56*/@ServicepublicclassTestServiceImplextendsServiceImpl<TestDbMapper, Test>implementsTestService{@AutowiredprivateImportDataListenerimportDataListener;@SneakyThrows@OverridepublicvoidimportExcel(MultipartFilemultipartFile) {InputStreaminputStream=multipartFile.getInputStream();// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(inputStream, Test.class, importDataListener).sheet().doRead(); }}
八、Mapper
/*** @author wangzhenjun* @date 2022/10/26 17:07*/publicinterfaceTestDbMapperextendsBaseMapper<Test> {}
九、测试
准备Excel数据:
postman上传:
控制台打印:
数据库查看:
完美搞定!!
十、总结
这样就完成了easyExcel批量导入Excel到数据库,还是有很多要注意的点:
自定义转换器监听器不要单例保存数据库采用批量版本差距