Hi,大家好,我是抢老婆酸奶的小肥仔。
在我们日常开发中,有个功能几乎是没办法绕开的,那就是Excel数据的导入。当然也有很多工具支持导入导出,比如:Apache POI,jxl,由于Apache POI在加载大量数据时会出现OOM,因此阿里在其基础上进行了封装形成了EasyExcel,这便是我们今天要说的主角。
1、EasyExcel简介
官网:https://easyexcel.opensource.alibaba.com/
按照官网介绍,EasyExcel具有以下特点:
- 基于java实现的
- 快捷、简洁、解决大文件内存溢出(即OOM)
- 不用考虑性能、内存等因素情况下,快速完成Excel的读、写等功能。
从简介中,知道使用EasyExcel操作Excel时,我们只需要关注业务本身即可。
2、程序实现
上述我们稍稍简介了下EasyExcel,接下来我们来看看怎样使用。
引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
2.1 读取Excel
按照官网给出的例子,一共有4种读取Excel写法,通过官网的例子来看,其实所有例子都是要以ReadListener<T>
为基础。我们以官网提供的方法一(PageReadListener)为例来说下EasyExcel读取的实现。
EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
for (DemoData demoData : dataList) {
log.info("读取到一条数据{}", JSON.toJSONString(demoData));
}
}),1000).sheet().doRead();
这种方法直接使用了PageReadListener监听器,我们来看看PageReadListener源码:
public class PageReadListener<T> implements ReadListener<T> {
public static int BATCH_COUNT = 100;
private List<T> cachedDataList;
private final Consumer<List<T>> consumer;
private final int batchCount;
public PageReadListener(Consumer<List<T>> consumer) {
this(consumer, BATCH_COUNT);
}
public PageReadListener(Consumer<List<T>> consumer, int batchCount) {
this.cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
this.consumer = consumer;
this.batchCount = batchCount;
}
public void invoke(T data, AnalysisContext context) {
this.cachedDataList.add(data);
if (this.cachedDataList.size() >= this.batchCount) {
this.consumer.accept(this.cachedDataList);
this.cachedDataList = ListUtils.newArrayListWithExpectedSize(this.batchCount);
}
}
public void doAfterAllAnalysed(AnalysisContext context) {
if (CollectionUtils.isNotEmpty(this.cachedDataList)) {
this.consumer.accept(this.cachedDataList);
}
}
}
在PageReadListener中给了两个构造方法,两者唯一区别就是一个采用了默认的读取条数,一个采用了用户自定义的条数。
在源码中还有两个方法:invoke
和doAfterAllAnalysed
,他们都是实现ReadListener接口里面定义的方法。
invoke
:表示每解析完一条数据就会调用该初始方法,因此很多条件筛选或业务我们可以放在里面实现。
doAfterAllAnalysed
:表示每解析完一个sheet页后调用该方法。
从源码中知道,invoke方法中当数组的长度大于等于设置的长度时,则执Consumer,执行完成后在进行初始化集合;doAfterAllAnalysed方法中在获取完数据后,判断当前集合时候还有数据,有的话则执行Consumer。
2.2 整合Mybutis-plus
我们通过方法一了解了PageReadListener的实现过程,那么我们也可以仿照PageReadListener、结合Mybatis-plus来实现一个通用批量导入的功能。
我们来简单引入Mybatis-plus。
1、引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
2、Mybatis-plus配置
/**
* @author: jiangjs
* @description: mybatis-plus配置
* @date: 2023/10/20 15:16
**/
@Configuration
@MapperScan(value = "com.**.mapper.**")
public class MybatisPlusConfig {
}
3、数据库开启批量插入
在Mysql批量插入数据时,我们一般采用insert ino table (xxx,xxxx) valus (11,22),(33,44),这种方式减少了数据库的连接,提高插入效率,而mybatis这样执行批处理需要在数据库url配置上添加rewriteBatchedStatements=true
,进行批处理开启。
url: jdbc:mysql://127.0.0.1:3308/xxxxx?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false&useUnicode=true&rewriteBatchedStatements=true
上述准备工作做好后,我们开始编写代码。