使用easyexcel进行分类存储数据库

    技术2022-07-11  92

    引入依赖 <dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.1</version> </dependency> <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> </dependencies> 创建与Excel表格对应的实体类 , 通过@ExcelProperty(index = 0)注解中的index指定字段与Excel表中的哪儿列相对应 package com.starcpdk.edu.eduservice.entity.excel; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; @Data public class SubjectData { @ExcelProperty(index = 0) private String oneSubjectName; @ExcelProperty(index = 1) private String twoSubjectName; } 使用代码生成器快速构建service , controller , mapper层 , 具体使用方式参考这篇文章 package com.starcpdk.demo; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import org.junit.Test; /** * @author * @since 2018/12/13 */ public class CodeGenerator { @Test public void run() { // 1、创建代码生成器 AutoGenerator mpg = new AutoGenerator(); // 2、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir("I:\\Projects\\IND_Projects\\cpdkedu\\Back_End\\cpdkedu\\cpdkedu_parent\\service\\service_edu" + "/src/main/java/"); gc.setAuthor("姚云峰"); gc.setOpen(false); //生成后是否打开资源管理器 gc.setFileOverride(false); //重新生成时文件是否覆盖 gc.setServiceName("%sService"); //去掉Service接口的首字母I gc.setIdType(IdType.ID_WORKER_STR); //主键策略 gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型 gc.setSwagger2(true);//开启Swagger2模式 mpg.setGlobalConfig(gc); // 3、数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/cpdkedu?serverTimezone=GMT+8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 4、包配置 PackageConfig pc = new PackageConfig(); //com.starcpdk.edu.eduservice pc.setParent("com.starcpdk.edu"); pc.setModuleName("eduservice"); //模块名 //com.starcpdk.edu.eduservice.controller pc.setController("controller"); //com.starcpdk.edu.eduservice.entity pc.setEntity("entity"); //com.starcpdk.edu.eduservice.service pc.setService("service"); //com.starcpdk.edu.eduservice.mapper pc.setMapper("mapper"); mpg.setPackageInfo(pc); // 5、策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("edu_subject"); strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略 strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀 strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略 strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作 strategy.setRestControllerStyle(true); //restful api风格控制器 strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符 mpg.setStrategy(strategy); // 6、执行 mpg.execute(); } }

    由于使用easyExcel读操作需要使用到监听器 , 监听器是在service层new出来的 , 所以监听器SubjectExcelListener无法使用spring管理 , 因此无法使用@Autowired自动装配注入service层对象进行数据库的增删改查操作 , 那么我们该怎么解决呢?

    解决方式如下:

    那么我们就通过从controller传递注入的service层对象subjectService实现注入监听器中

    controller层传递到service层 , service层new的监听器对象通过有参构造将subjectService传递到监听器SubjectExcelListener中.


    编写controller内容 , 使用MultipartFile 上传文件 , 将上传的文件和service层的对象传到service层 package com.starcpdk.edu.eduservice.controller; import com.starcpdk.edu.commonutils.R; import com.starcpdk.edu.eduservice.service.EduSubjectService; 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 姚云峰 * @since 2020-07-01 */ @RestController @RequestMapping("/eduservice/subject") @CrossOrigin public class EduSubjectController { @Autowired private EduSubjectService subjectService; //添加课程分类 //获取到上传的文件 , 把文件内容读取出来 @PostMapping("addSubject") public R addSubject(MultipartFile file){ //上传过来的excel文件 subjectService.saveSubject(file , subjectService); return R.ok(); } } service层书写saveSubject方法 , 通过file.getInputStream();方法获取文件的流 , 通过EasyExcel.read(in , SubjectData.class , new SubjectExcelListener(subjectService)).sheet().doRead();方法实现读excel表中数据。具体的逻辑处理在监听器中进行 package com.starcpdk.edu.eduservice.service.impl; import com.alibaba.excel.EasyExcel; import com.starcpdk.edu.eduservice.entity.EduSubject; import com.starcpdk.edu.eduservice.entity.excel.SubjectData; import com.starcpdk.edu.eduservice.listener.SubjectExcelListener; import com.starcpdk.edu.eduservice.mapper.EduSubjectMapper; import com.starcpdk.edu.eduservice.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 姚云峰 * @since 2020-07-01 */ @Service public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService { //添加课程分类 @Override public void saveSubject(MultipartFile file , EduSubjectService subjectService) { try { //文件输入流 InputStream in = file.getInputStream(); //调用方法进行读取 EasyExcel.read(in , SubjectData.class , new SubjectExcelListener(subjectService)).sheet().doRead(); }catch (Exception e){ e.printStackTrace(); } } } 书写监听器 , 比对excel表中的名字和级数是否存在表中进行筛选是否加入数据库 , 监听器是继承AnalysisEventListener类的 , 泛型是与excel表对应的实体类 package com.starcpdk.edu.eduservice.listener; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.starcpdk.edu.eduservice.entity.EduSubject; import com.starcpdk.edu.eduservice.entity.excel.SubjectData; import com.starcpdk.edu.eduservice.service.EduSubjectService; import com.starcpdk.edu.service_base.exceptionhandler.MyException; public class SubjectExcelListener extends AnalysisEventListener<SubjectData> { //因为SubjectExcelListener不能交给spring进行管理 , 需要自考管理 , 不能注入其他对象 //不能用mapper实现数据库操作 public EduSubjectService subjectService; public SubjectExcelListener() {} public SubjectExcelListener(EduSubjectService subjectService) { this.subjectService = subjectService; } //读取excel内容 , 一行一行读取 @Override public void invoke(SubjectData subjectData, AnalysisContext analysisContext) { if (subjectData == null){ throw new MyException(20001 , "文件数据为空"); } //一行一行读取 , 每次读取有两个值 , 第一个值以及分类 , 第二个值为二级分类 //判断一级分类是否重复 EduSubject existOneSubject = this.existOneSubject(subjectService, subjectData.getOneSubjectName()); if (existOneSubject == null){//没有相同一级分类 , 进行添加 existOneSubject = new EduSubject(); existOneSubject.setParentId("0"); existOneSubject.setTitle(subjectData.getOneSubjectName());//一级分类名称 subjectService.save(existOneSubject); } //获取一级分类的id值 String pid = existOneSubject.getId(); //添加二级分类 //判断二级分类是否存在 EduSubject existTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid); if (existTwoSubject == null){ existTwoSubject = new EduSubject(); existTwoSubject.setParentId(pid); existTwoSubject.setTitle(subjectData.getTwoSubjectName());//二级分类名称 subjectService.save(existTwoSubject); } } //判断一级分类不能重复添加 private EduSubject existOneSubject(EduSubjectService subjectService , String name){ QueryWrapper<EduSubject> wrapper = new QueryWrapper<>(); wrapper.eq("title" , name); wrapper.eq("parent_id" , "0"); EduSubject oneSubject = subjectService.getOne(wrapper); return oneSubject; } //判断二级分类不能重复添加 private EduSubject existTwoSubject(EduSubjectService subjectService , String name , String pid){ QueryWrapper<EduSubject> wrapper = new QueryWrapper<>(); wrapper.eq("title" , name); wrapper.eq("parent_id" , pid); EduSubject twoSubject = subjectService.getOne(wrapper); return twoSubject; } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } }

    具体思路:

    Excel表中的数据分为两列 , 通过@ExcelProperty(index = 0)中的index与excel表中的列数相对应 , 下边的代码则表示excel表中的第一列的数据会赋值给oneSubjectName即以及分类中,同理二级分类也是如此

    在数据库表中如何区分一级分类二级分类呢?如何让其想对应上呢?

    通过id与parent_id两个字段所决定 , 一级分类的parent_id是0 , 二级分类的parent_id是一级分类相对应的id值

    在监听器中通过读取excel表中数据 , 对应根据分类的名称和分类的级数去数据库查找数据 , 若查到 , 说明该分类在数据库中已存在 , 无需再次插入 , 若查询返回值为空 ,则将此条数据插入到数据库中

    @ExcelProperty(index = 0) private String oneSubjectName; @ExcelProperty(index = 1) private String twoSubjectName;

    Processed: 0.011, SQL: 9