[TOC]
复杂EasyExcel的导入导出。使用方案:Alibaba EasyExcel。
> 官网地址:https://www.yuque.com/easyexcel/doc/easyexcel
## 优点分析
- 可以实现自动按名称匹配Excel表头,即使出现了表头顺序变化;
- 表头与实体类中注解不匹配时,不会报错,较为友好,只是不能匹配的数据会返回null。
步骤解释:
1. 创建接收数据的model或者pojo;
2. 定义一个listener用来监听解析完成的数据,并完成数据入库;
## 使用Demo
前提:引用对应的maven。
```
<easyexcel.version>2.2.6</easyexcel.version>
```
```
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
```
### 1-定义数据接收对象
```
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.mrzihan.resc.util.excel.CustomStringIntegerConverter;
import lombok.Data;
/**
* 对应模板:2020年省目录所有字段导出,共29列
* Description:资源导入映射实体对象
* Author:mrzihan
* CreateDate:2018/12/6
* "@ExcelProperty"使用注意事项:
* - 属性名首字母不能大写,否则取不到值
*/
@Data
public class ResImportByNamePojo {
@ExcelProperty("区域")
private String areaName;
@ExcelProperty("数源单位")
private String supplyDept;
@ExcelIgnore
private String supplyDeptId;
@ExcelProperty("信息资源名称")
private String resTitle;
@ExcelProperty("归集表英文名称")
private String tableName;
@ExcelIgnore
private String resKeyword;
@ExcelProperty("信息资源摘要")
private String abstractInfo;
@ExcelProperty("所属系统名称")
private String sysName;
@ExcelProperty("信息资源格式")
private String resFormat;
@ExcelProperty("重点领域分类")
private String fieldName;
@ExcelProperty("更新频率")
private String updateCycle;
@ExcelProperty("修改日期")
private String CreateDate;
@ExcelProperty("是否引用省级")
private String extended;
@ExcelProperty("状态")
private String sts;
@ExcelProperty("归集状态")
private String collectSts;
@ExcelIgnore
private String transType;
@ExcelProperty("英文名称")
private String colName;
@ExcelProperty("中文名称")
private String colDesc;
@ExcelProperty("数据类型")
private String colType;
@ExcelProperty("字段描述")
private String colComment;
@ExcelProperty(value = "数据长度",converter = CustomStringIntegerConverter.class)
private Integer colLength;
@ExcelProperty("默认值")
private String colDefaultValue;
@ExcelProperty("是否字典项")
private String ifDictionary;
@ExcelProperty("共享属性")
private String shareType;
@ExcelProperty("共享条件")
private String shareCondition;
@ExcelProperty("开放属性")
private String ifPublic;
@ExcelProperty("开放条件")
private String publicCondition;
@ExcelProperty("是否可为空")
private String emptyEnable;
@ExcelProperty("是否主键")
private String ifKey;
@ExcelProperty("是否归集")
private String ifAllowGj;
}
```
### 2-定义一个listener用来监听解析完成的数据
定义一个listener用来监听解析完成的数据,并调用业务service分批次插入数据库。
```
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.mrzihan.common.exception.ApplicationException;
import com.mrzihan.common.exception.DuplicateException;
import com.mrzihan.common.model.DeptModel;
import com.mrzihan.common.model.UserModel;
import com.mrzihan.common.util.ApplicationContext;
import com.mrzihan.common.util.DictionaryHelper;
import com.mrzihan.resc.constants.BusinessConstans;
import com.mrzihan.resc.enums.DictionaryEnum;
import com.mrzihan.resc.model.ImportHistory;
import com.mrzihan.resc.model.resRegist.RegistFormModel;
import com.mrzihan.resc.model.resRegist.ResTransModel;
import com.mrzihan.resc.model.resRegist.TransColumnsModel;
import com.mrzihan.resc.pojo.ResImportByNamePojo;
import com.mrzihan.resc.service.IDdeptService;
import com.mrzihan.resc.service.IPersistentObjectService;
import com.mrzihan.resc.service.resManager.IResRegistService;
import org.apache.commons.lang3.StringUtils;
import org.sevenstar.persistent.db.exception.PersistentException;
import org.sevenstar.persistent.db.ibatis.ApplicationUtil;
import org.sevenstar.persistent.db.ibatis.IbatisDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import java.util.*;
/**
* 2020年新版省平台目录导入功能
* Created by mrzihan on 2020/6/25.
* connect to cowboy2014@qq.com
*/
public class StandardResImportListener extends AnalysisEventListener<ResImportByNamePojo> {
private static final Logger logger = LoggerFactory.getLogger(StandardResImportListener.class);
public List<ResImportByNamePojo> list = new ArrayList<ResImportByNamePojo>();
//定义一个名字按照资源分条导入
public String importingResName = "";
private static String REGEX_CHINESE = "[\u4e00-\u9fa5]";// 中文正则
ImportHistory history = new ImportHistory();
private final String fileName;
private final UserModel user;
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private final IPersistentObjectService persistent;
private final IResRegistService registService;
private final IDdeptService deptService;
//记录导入的资源数量
private long resCount = 0;
private long validCount = 0;
public StandardResImportListener(String fileName) {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
persistent = (IPersistentObjectService) ApplicationUtil.getBean("persistentObjectService");
registService = (IResRegistService) ApplicationUtil.getBean("resRegistService");
deptService = (IDdeptService) ApplicationUtil.getBean("deptService");
user = ApplicationContext.getUserDomain();
this.fileName = fileName;
this.init();
}
/**
* 初始化日志表
*/
private void init(){
history.setFile_name(fileName);
history.setCreateUser(user);
history.setDept_id(user.getDeptDomain().getId());
// history.setData_amount((long) objects.size());
history.setCreate_date(new Date());
history.setSts("N");
history.setValid_amount(0L);
history.setResource_amount(0L);
history.setType("res");
persistent.insert(history);
}
/**
* 所有数据解析完成了 都会来调用
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
logger.info("所有数据解析完成!");
history.setValid_amount(resCount);
history.setResource_amount(validCount);
history.setDept_id(user.getDeptDomain().getId());
history.setSts("U");
persistent.update(history);
}
/**
* 加上存储数据库
*/
private void saveData() {
logger.info("{}条数据,开始存储数据库!", list.size());
/** 资源导入阶段 **/
try {
//1- registFormModel转换
RegistFormModel registFormModel = convertResFormAndSave();
/** 2- 插入资源数据源信息 **/
if (null != registFormModel.getId()){
ResTransModel resTrans = genTransAndSave(registFormModel);
//3- 信息项信息存储
for (ResImportByNamePojo pojo: list){
genColumnsAndSave(pojo, resTrans);
}
}
}catch (Exception e){
String errorMsg = "导入中断:"+e.getMessage();
history.setMessage(errorMsg);
persistent.update(history);//此处事务未控制
// 异常增强,中断执行
throw new ApplicationException(errorMsg);
}
validCount++;
resCount++;
//记录导入日志
logger.info("存储数据库成功!");
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(ResImportByNamePojo data, AnalysisContext context) {
logger.info("解析到一条数据:{}", JSON.toJSONString(data));
// 达到BATCH_COUNT了,即完整的识别了一个资源,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (StringUtils.isEmpty(importingResName)){
importingResName = data.getResTitle();
list.add(data);
}else if (StringUtils.isNotEmpty(importingResName) && !importingResName.equals(data.getResTitle())){
saveData();
// 存储完成清理 list
list.clear();
importingResName = data.getResTitle();
list.add(data);
}else {
list.add(data);
}
}
}
```
### 3-service层业务调用入口
```
/**
* 根据省模板导入
* @param request
* @return
*/
@Override
public Map<String, Object> uploadStandardData(HttpServletRequest request) {
Map<String, Object> result = new HashMap<>();
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
if (multipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
MultipartFile excel = multiRequest.getFile("file");
//版本1:ResImportPojo.class 版本2:ResImportByNamePojo.class
StandardResImportListener listener = new StandardResImportListener(excel.getOriginalFilename());
try {
EasyExcel.read(excel.getInputStream(), ResImportByNamePojo.class, listener).sheet(0).headRowNumber(1).doRead();
result.put("msg", "格式校验通过");
} catch (Exception e) {
e.printStackTrace();
result.put("msg", e.getMessage());
}
} else {
result.put("flag", false);
result.put("msg", "请上传正确格式的Excel文件");
return result;
}
result.put("flag", true);
return result;
}
- 简介
- 更新说明
- 其他作品
- 第一部分 Java框架基础
- 第一章 Java基础
- 多线程实战
- 尝试一下Guava带返回值的多线程处理类ListenableFuture
- LocalDate和Date有什么区别
- JAVA8接口增强实践
- 第二章 Spring框架基础
- MVC究竟是个啥?
- @ApiImplicitParam
- 七种方式,教你在SpringBoot初始化时搞点事情!
- Spring事务状态
- maven
- Mybatis小总结
- mybatis-plus的使用
- 第三章 SpringSecurity实战
- 基于SpringSecurity+jwt的用户认证
- spring-security-oauth2
- 第四章 数据库
- mysql
- mysql授权
- mysql数据库三个关键性能指标--TPS\QPS\IOPS
- 梳理一下那些年Mysql的弱语法可能会踩的坑
- 关于Mysql的“字符串”数值的转换和使用
- 凭这一文咱把事务讲透
- Mysql性能优化
- 查询性能优化
- 不常用的一些语法
- elasticsearch
- elasticsearch文档操作
- 索引的基本操作
- java操作ElaticSearch
- elasticsearch中的各种查询
- DB与ES混合应用可能存在的问题及解决方案探索
- 使用es必须要知道的一些知识点:索引篇
- Es中的日期操作
- MongoDB
- 入门篇(了解非关系型数据库 NoSQL - MongoDB)
- 集群分片 (高级篇)
- 互联网大厂的建表规范
- 第五章 中间件
- nginx
- nginx动静分离配置,这个雷你踩过吗?
- Canal
- Sharding-jdbc
- 水平分库实践
- kafka
- 第六章 版本管理
- git
- Not currently on any branch 情况提交版本
- 第七章 IO编程
- 第八章 JVM实战调优
- jvisualvm
- jstat
- 第二部分 高级项目实战篇
- 第一章 微信开发实战
- 第二章 文件处理
- 使用EasyExcel处理导入导出
- 第三章 踩坑指南
- 邮件发送功能
- 第三部分 架构实战篇
- 第一章 架构实战原则
- 接口防止重复调用的一种方案
- 第二章 高并发缓存一致性管理办法
- 第三章 异地多活场景下的数据同步之道
- 第四章 用户体系
- 集成登录
- auth-sso的管理
- 第五章 分库分表场景
- 第六章 秒杀与高并发
- 秒杀场景
- 第七章 业务中台
- 中台的使用效果是怎样的?
- 通用黑白名单方案
- 第八章 领域驱动设计
- 第十一章 微服务实战
- Nacos多环境管理之道
- logback日志双写问题及Springboot项目正确的启动方式
- 第四部分 优雅的代码
- java中的链式编程
- 面向对象
- 开发原则
- Stream操作案例分享
- 注重性能的代码
- 第五部分 谈谈成长
- 新手入门指北
- 不可不知的调试技巧
- 构建自己的知识体系
- 我是如何做笔记的
- 有效的提问
- 谨防思维定势
- 学会与上级沟通
- 想清楚再去做
- 碎片化学习
- 第六部分 思维导图(付费)
- 技术基础篇
- 技术框架篇
- 数据存储篇
- 项目实战篇
- 第七部分 吾爱开源
- 7-1 麻雀聊天
- 项目启动
- 前端登录无请求问题解决
- websocket测试
- 7-2 ocp微服务框架
- evm框架集成
- 项目构建与集成
- zentao-center
- 二次开发:初始框架的搭建
- 二次开发:增加细分菜单、权限到应用
- 7-3 书栈网
- 项目启动
- 源码分析
- 我的书架
- 文章发布机制
- IM
- 第八章 团队管理篇
- 大厂是怎么运作的
- 第九章 码山有道
- 简历内推
- 联系我内推
- 第十章 学点前端
- Vue