SpringBoot集成EasyExcel的应用场景分析
作者:zhz小白弟弟 时间:2023-01-09 07:47:45
1、介绍
官网地址:https://www.yuque.com/easyexcel
特点:
1、Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是 非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或 者JVM频繁的full gc。
2、EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减 少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一 行行读取数据,逐个解析。
3、EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理 (AnalysisEventListener)。
2、应用场景
1、数据导入:减轻录入工作量
2、数据导出:统计信息归档
3、数据传输:异构系统之间数据传输
3、要实现的效果
sql
CREATE TABLE `edu_subject` (
`id` char(19) NOT NULL COMMENT '课程类别ID',
`title` varchar(10) NOT NULL COMMENT '类别名称',
`parent_id` char(19) NOT NULL DEFAULT '0' COMMENT '父ID',
`sort` int unsigned NOT NULL DEFAULT '0' COMMENT '排序字段',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=COMPACT COMMENT='课程科目';
转成->
3、使用
3.1、pom依赖导入
温馨提示:以下版本不能更换,换了可能会不行
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
3.2、controller
package com.zhz.serviceedu.controller;
import com.zhz.common.utils.R;
import com.zhz.serviceedu.service.EduSubjectService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* <p>
* 课程科目 前端控制器
* </p>
*
* @author zhz
* @since 2021-07-03
*/
@RestController
@RequestMapping("/eduservice/subject")
@CrossOrigin
@Api(tags = "课程科目")
public class EduSubjectController {
@Autowired
private EduSubjectService eduSubjectService;
/**
* 添加课程分类,获取上传过来的文件,把文件内容读取出来
*/
@PostMapping("/addSubject")
@ApiOperation(value = "添加课程分类,获取上传过来的文件,把文件内容读取出来")
public R addSubject(MultipartFile file){
//上传过来的excel文件
eduSubjectService.saveSubject(file,eduSubjectService);
return R.ok();
}
}
3.3、interface
package com.zhz.serviceedu.service;
import com.zhz.serviceedu.entity.EduSubject;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
/**
* <p>
* 课程科目 服务类
* </p>
*
* @author zhz
* @since 2021-07-03
*/
public interface EduSubjectService extends IService<EduSubject> {
/**
* 添加课程信息
*
* @author zhz
* @date 2021/07/02 02:18
* @param file 文件对象,用于获取excel文件
* @param eduSubjectService 方便 * 部分引用
*/
void saveSubject(MultipartFile file, EduSubjectService eduSubjectService);
}
3.4、impl
package com.zhz.serviceedu.service.impl;
import com.alibaba.excel.EasyExcel;
import com.zhz.serviceedu.entity.EduSubject;
import com.zhz.serviceedu.entity.excel.SubjectData;
import com.zhz.serviceedu.listener.SubjectExcelListener;
import com.zhz.serviceedu.mapper.EduSubjectMapper;
import com.zhz.serviceedu.service.EduSubjectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
/**
* <p>
* 课程科目 服务实现类
* </p>
*
* @author zhz
* @since 2021-07-03
*/
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
/**
* 添加课程信息
*
* @param file 文件对象,用于获取excel文件
* @param eduSubjectService
* @author zhz
* @date 2021/07/02 02:18
*/
@Override
public void saveSubject(MultipartFile file, EduSubjectService eduSubjectService) {
try {
//文件输入流
InputStream in = file.getInputStream();
//调用方法进行读取
EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(eduSubjectService)).sheet().doRead();
}catch (Exception e){
e.printStackTrace();
}
}
}
3.5、listener
package com.zhz.serviceedu.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhz.servicebase.execptionhandler.GuliException;
import com.zhz.serviceedu.entity.EduSubject;
import com.zhz.serviceedu.entity.excel.SubjectData;
import com.zhz.serviceedu.service.EduSubjectService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
/**
* @author zhouhengzhe
* @Description: excel *
* @date 2021/7/3上午2:28
*/
@Slf4j
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
/**
* 创建有参数构造,传递subjectService用于操作数据库
* 因为SubjectExcelListener不能交给spring去管理,所以需要自己new,不能注入对象
* 此处一定要是public,不然永远获取不到对象
*/
public EduSubjectService eduSubjectService;
public SubjectExcelListener() {
}
public SubjectExcelListener(EduSubjectService eduSubjectService) {
this.eduSubjectService = eduSubjectService;
}
/**
* 读取excel内容,一行一行进行读取,此处全是业务处理
* @param subjectData
* @param analysisContext
*/
@Override
public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
log.info("进入方法调用");
if (StringUtils.isEmpty(subjectData)){
throw new GuliException(20001,"文件数据为空");
}
//一行一行去读取excel内容,每次读取有两个值,第一个值为一级分类,第二个值为二级分类
//判断一级分类是否重复
EduSubject existOneSubject = this.existOneSubject(eduSubjectService, subjectData.getOneSubjectName());
if (StringUtils.isEmpty(existOneSubject)){
existOneSubject=new EduSubject();
existOneSubject.setParentId("0");
//一级分类名称
existOneSubject.setTitle(subjectData.getOneSubjectName());
eduSubjectService.save(existOneSubject);
}
//获取一级分类的pid值
String pid=existOneSubject.getId();
//添加二级分类
// 判断二级分类是否重复
EduSubject existTwoSubject = this.existTwoSubject(eduSubjectService, subjectData.getTwoSubjectName(), pid);
if (StringUtils.isEmpty(existTwoSubject)){
existTwoSubject=new EduSubject();
existTwoSubject.setParentId(pid);
//二级分类名称
existTwoSubject.setTitle(subjectData.getTwoSubjectName());
eduSubjectService.save(existTwoSubject);
}
}
/**
* 读取完成后执行
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
/**
* 判断一级分类是否重复
* 因为课程的的parent_id为0时,代表是一级分类,并且一级分类数据不重复
* @param eduSubjectService
* @param name
* @return
*/
private EduSubject existOneSubject(EduSubjectService eduSubjectService,String name){
QueryWrapper<EduSubject> wrapper=new QueryWrapper<>();
wrapper.eq("title",name);
wrapper.eq("parent_id","0");
EduSubject subject = eduSubjectService.getOne(wrapper);
return subject;
}
/**
* 判断二级分类是否重复
*
* @param eduSubjectService
* @param name
* @param pid
* @return
*/
private EduSubject existTwoSubject(EduSubjectService eduSubjectService,String name,String pid){
QueryWrapper<EduSubject> wrapper=new QueryWrapper<>();
wrapper.eq("title",name);
wrapper.eq("parent_id",pid);
EduSubject eduSubject = eduSubjectService.getOne(wrapper);
return eduSubject;
}
}
3.6、小细节,实体类pojo
因为mybatisplus生成的实体类的主键生成策略是IdType.ID_WORKER,所以需要修改成IdType.ID_WORKER_STR,否则会有转换问题
package com.zhz.serviceedu.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
* 课程科目
* </p>
*
* @author zhz
* @since 2021-07-03
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="EduSubject对象", description="课程科目")
public class EduSubject implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "课程类别ID")
@TableId(value = "id", type = IdType.ID_WORKER_STR)
private String id;
@ApiModelProperty(value = "类别名称")
private String title;
@ApiModelProperty(value = "父ID")
private String parentId;
@ApiModelProperty(value = "排序字段")
private Integer sort;
@ApiModelProperty(value = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
}
3.7、其余,swagger集成,R类统一返回,统一日志等,请看我其他博客
统一日志:https://blog.csdn.net/zhouhengzhe/article/details/118078080
统一返回值:https://blog.csdn.net/zhouhengzhe/article/details/118065066
统一异常:https://blog.csdn.net/zhouhengzhe/article/details/118064739
swagger集成:https://blog.csdn.net/zhouhengzhe/article/details/118063779
来源:https://blog.csdn.net/zhouhengzhe/article/details/118427902