1、EasyExcel简介
官网:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网
2、代码实战
首先引入jar包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
2.1 读取Excel
2.1.1 使用PageReadListener
这种方式代码简洁,通俗易懂,直接看示例:
public void readExcelFile2(MultipartFile file) {
AtomicInteger count = new AtomicInteger(0);
try {
int batch = 10;
EasyExcel.read(file.getInputStream(), SharePathApproveModule.class, new PageReadListener<SharePathApproveModule>(list -> {
System.out.println("已完成" + list.size() + "条数据读取...");
for (SharePathApproveModule module : list) {
System.out.println("读取到" + count.incrementAndGet() + "条数据=>" + JSONObject.toJSONString(module));
}
//批量写入
//sharePathApproveMapper.insertBatch(list);
}, batch)).sheet(0).doRead();
//sheetNo参数不传默认0,读取第一个sheet;填0也是读取第1个sheet;填1即读取第2个sheet
} catch (Exception e) {
log.error("读取Excel数据异常,", e);
}
}
这种方式是直接使用了PageReadListener监听器,有兴趣的同学可以自行解读PageReadListener源码,这里我只把源码粘贴出来,本文主要讲实战,就不过多说PageReadListener。
import java.util.List;
import java.util.function.Consumer;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.util.ListUtils;
import org.apache.commons.collections4.CollectionUtils;
/**
* page read listener
*
* @author Jiaju Zhuang
*/
public class PageReadListener<T> implements ReadListener<T> {
/**
* Default single handle the amount of data
*/
public static int BATCH_COUNT = 100;
/**
* Temporary storage of data
*/
private List<T> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* consumer
*/
private final Consumer<List<T>> consumer;
/**
* Single handle the amount of data
*/
private final int batchCount;
public PageReadListener(Consumer<List<T>> consumer) {
this(consumer, BATCH_COUNT);
}
public PageReadListener(Consumer<List<T>> consumer, int batchCount) {
this.consumer = consumer;
this.batchCount = batchCount;
}
@Override
public void invoke(T data, AnalysisContext context) {
cachedDataList.add(data);
if (cachedDataList.size() >= batchCount) {
consumer.accept(cachedDataList);
cachedDataList = ListUtils.newArrayListWithExpectedSize(batchCount);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if (CollectionUtils.isNotEmpty(cachedDataList)) {
consumer.accept(cachedDataList);
}
}
}
简单的说一下PageReadListener监听器的两个方法:invoke和doAfterAllAnalysed;他们都是实现ReadListener接口里面定义的方法。
invoke:表示每解析完一条数据就会调用该初始方法,因此很多条件筛选或业务我们可以放在里面实现。
doAfterAllAnalysed:表示每解析完一个sheet页后调用该方法。
从源码中知道,invoke()中当数组的长度大于等于设置的长度时,则执Consumer,执行完成后在进行初始化集合;
doAfterAllAnalysed()中在获取完数据后,判断当前集合时候还有数据,有的话则执行Consumer。
2.1.2 自定义监听器
我们可以通过继承AnalysisEventListener类来自定义监听器,重新里面的invoke和doAfterAllAnalysed方法。
首先,新建一个Module
/**
* @description
* @date 2024-07-10 18:02
**/
@Data
public class ShareModule {
@ExcelProperty(value = "share路径", index = 0)
@ColumnWidth(value = 10)
private String sharePath;
@ExcelProperty(value = "权限", index = 1)
@ColumnWidth(value = 10)
private String access;
@ExcelProperty(value = "组名", index = 2)
@ColumnWidth(value = 10)
private String groupCn;
@ExcelProperty(value = "历史申请人", index = 3)
@ColumnWidth(value = 20)
private String applicants;
}
接着,定义Mapper和Mapper.xml,这里我们只写一个批量插入的方法,使用
insert into table_name(column1,column2,column3) values(x,x,x),(x,x,x),.....
这种方式减少了数据库的连接,提高插入效率,而mybatis这样执行批处理需要在数据库url配置上添加
rewriteBatchedStatements=true
,进行批处理开启。
public interface ShareMapper {
int insertBatch(@Param("shareModules")List<ShareModule> shareModules);
int clearTableData();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ShareMapper">
<insert id="insertBatch">
INSERT INTO share_path_approve (share_path, access, group_cn, applicants) VALUES
<foreach collection="shareModules" index="index" item="po" separator=",">
(#{po.sharePath}, #{po.access}, #{po.groupCn}, #{po.applicants})
</foreach>
</insert>
<update id="clearTableData">
truncate table share_path_approve
</update>
</mapper>
然后,就是自定义监听器,配将这个监听器交给Spring容器
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.wangpeng.mapper.SharePathApproveMapper;
import com.example.wangpeng.po.module.SharePathApproveModule;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* @description share auto
* @date 2024-07-10 10:30
*/
@Slf4j
public class ShareListener extends AnalysisEventListener<ShareModule> {
private List<ShareModule> cacheData = new ArrayList<>();
private static final int BATCH_COUNT = 8;
private final ShareMapper shareMapper;
public SharePathApproveListener(ShareMapper shareMapper) {
this.shareMapper= shareMapper;
}
@Override
public void invoke(ShareModule shareModule, AnalysisContext analysisContext) {
cacheData.add(shareModule);
if (cacheData.size() >= BATCH_COUNT) {
log.info("保存数据--share auto-----{}条", cacheData.size());
saveData();
// 可以清理缓存数据
cacheData = new ArrayList<>();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// 处理最后未达到8条数据的插入
if (!cacheData.isEmpty()) {
log.info("保存数据--share auto-----{}条", cacheData.size());
saveData();
}
}
private void saveData() {
// 这里可以使用MyBatis的批量插入方法
shareMapper.insertBatch(cacheData);
}
}
import com.example.wangpeng.excel.SharePathApproveListener;
import com.example.wangpeng.mapper.SharePathApproveMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @description
* @date 2024-07-10 18:17
*/
@Configuration
public class EasyExcelConfig {
@Bean
public ShareListener shareListener(ShareMapper shareMapper) {
return new shareListener(shareMapper);
}
}
最后一步,就是编写Service和实现类、控制层接口代码
public interface ExcelService {
ResponseResult<?> importExcel(MultipartFile file, Integer type);
}
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.excel.ShareListener;
import com.example.demo.mapper.ShareMapper;
import com.example.demo.po.module.ShareModule;
import com.example.demo.response.ResponseResult;
import com.example.demo.service.ExcelService;
import lombok.extern.log4j.Log4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
/**
* @description
* @date 2024-06-03 15:57
*/
@Log4j
@Service
public class ExcelServiceImpl implements ExcelService {
@Autowired
private SharePathApproveMapper sharePathApproveMapper;
@Override
public ResponseResult<?> importExcel(MultipartFile file, Integer type) {
try (InputStream inputStream = file.getInputStream()) {
if (0 == type) {
//全量覆盖
sharePathApproveMapper.clearTableData();
} //增量插入
EasyExcel.read(inputStream, ShareModule.class, new ShareListener(shareMapper))
.sheet()
.doRead();
return ResponseResult.success();
} catch (Exception e) {
log.info("ShareExcel并解析 异常!", e);
return ResponseResult.fail(e.getMessage());
}
}
}
@RestController
@RequestMapping(value = "/excel")
public class ExcelController {
@Autowired
private ExcelService excelService;
@PostMapping(value = "/import")
public ResponseResult<String> excelFile2(@RequestBody MultipartFile file, Integer type) {
excelService.importExcel(file, type);
return ResponseResult.success();
}
}
运行如下: