zoukankan      html  css  js  c++  java
  • easyexcel解析excel

      解析excel我们常用的应该就是poi了,这个比较坑,之前项目用过一下,这次我们使用一下阿里的easyexcel吧!easyexcel的使用很简单,比较类似于最开始解析xml使用的SAX,就是每读取文件一行,就直接进行处理,完全不需要文件都加载到内存中!!!

      easyexcel的官方文档

    1. 随意新建一个springboot项目,导入常见的依赖(基于springboot+mybatis+mybatisplus+lombok+swagger),然后再导入easyexcel:

    <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>2.1.1</version>
            </dependency>

    2. excel文件中的内容,比如淘宝商品的分类,肯定是分为一级,二级,三级分类等等,下图所示,一个一级分类对应多个二级分类,一个二级对应多个三级,等等

      我们就不弄那么麻烦了,以一个课程的分类为例,有一级分类二级分类,下图的excel文件所示,我们需要将这个excel文件上传,然后通过easyexcel解析出来,丢到数据库中,类似图中数据库表中的数据

            

    sql脚本如下::

    create table subject
    (
        id           char(19)     not null     comment '课程类别ID' primary key,
        title        varchar(10)  not null     comment '类别名称',
        parent_id    char(19)     default '0' not null comment '父ID'
    )comment '课程科目';
    
    create index idx_parent_id on subject (parent_id);

    3. controller代码:

    package com.protagonist.edu.controller;
    
    
    import com.protagonist.edu.service.SubjectService;
    import com.protagonist.responseVO.Result;
    import com.protagonist.responseVO.StatusCode;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    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;
    
    import javax.annotation.Resource;
    
    /**
     * <p>
     * 课程科目 前端控制器
     * </p>
     *
     * @author protagonist
     * @since 2020-10-01
     */
    @Api("课程分类的api")
    @RestController
    @RequestMapping("/edu/subject")
    @Slf4j
    public class SubjectController {
    
        @Resource
        private SubjectService subjectServiceImpl;
    
        //添加课程分类,上传excel然后进行读取,解析成对象
        @PostMapping("/addSubject")
        @ApiOperation(value = "上传excel文件中的一级分类和二级分类")
        public Result addSubject(MultipartFile file) {
            subjectServiceImpl.saveSubject(file);
            return new Result<>(true, StatusCode.OK, "添加课程分类成功");
        }
    
    }

    4.service和serviceImpl:

    public interface SubjectService extends IService<Subject> {
    
        /**
         * 添加课程分类
         * @param file
         */
        void saveSubject(MultipartFile file);
    
    }
    
    
    
    @Service
    @Slf4j
    public class SubjectServiceImpl extends ServiceImpl<SubjectMapper, Subject> implements SubjectService {
    
        @Override
        public void saveSubject(MultipartFile file) {
            try{
                InputStream inputStream = file.getInputStream();
                //读取excel
                EasyExcel.read(inputStream, ExcelSubjectData.class,new SubjectExcelListener(this)).sheet().doRead();
            }catch (Exception e){
                log.error("读取excel文件异常",e);
                throw new ProtagonistException(StatusCode.READ_FILE_ERROR,"读取excel文件异常");
    
            }
    
    
        }
    }

      

    5.entity

    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    @TableName("subject")
    @ApiModel(value="Subject对象", description="课程科目")
    public class Subject 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;
    
    }

    6.我们还需要一个类,这个类的每一个实例都对应excel中每一行数据,我们将每一行数据都转成一个对象之后,然后进行处理就方便了;

    package com.protagonist.edu.excel;
    
    import com.alibaba.excel.annotation.ExcelProperty;
    import lombok.Data;
    
    @Data
    public class ExcelSubjectData {
        /**
         * 一级分类
         */
        @ExcelProperty(index = 0)
        private String oneSubjectName;
    
        /**
         * 二级分类
         */
        @ExcelProperty(index = 1)
        private String twoSubjectName;
    }

    7.listener,之前说过easyexcel是每读一行数据都会处理一下, 所以这里需要来一个监听器:

    package com.protagonist.edu.entity.listener;
    
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.event.AnalysisEventListener;
    import com.alibaba.fastjson.JSON;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.protagonist.edu.entity.Subject;
    import com.protagonist.edu.excel.ExcelSubjectData;
    import com.protagonist.edu.service.SubjectService;
    import com.protagonist.responseVO.StatusCode;
    import com.protagonist.servicebase.exception.ProtagonistException;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.Objects;
    
    @Slf4j
    public class SubjectExcelListener extends AnalysisEventListener<ExcelSubjectData> {
        //这里必须来个构造器,将service传过来,然后再监听器中每处理一行就可以直接进行入库操作;
        //其实可以使用mybatis的批量入库的
        private SubjectService subjectService;
        public SubjectExcelListener(SubjectService subjectService){
            this.subjectService = subjectService;
        }
    
    
        //这个方法读取excel内容,一行一行读取,每一行都会触发这个方法,而excelSubjectData表示的就是excel中的一行数据
        //注意这里的第一行不会读,默认是表头
        @Override
        public void invoke(ExcelSubjectData excelSubjectData, AnalysisContext analysisContext) {
            log.info("每一行处理excel数据=============================");
            if (Objects.isNull(excelSubjectData)){
                throw new ProtagonistException(StatusCode.ERROR,"文件内容为空");
            }
            //获取每一行中的第一个数据和第二个数据,然后判断数据库中是否有该分类信息,没有的话才会插入数据库
            String oneSubjectName = excelSubjectData.getOneSubjectName();
            String twoSubjectName = excelSubjectData.getTwoSubjectName();
    
            //判断数据库中有没有一级分类,没有的话就插入一级分类
            Subject oneSubject = getOneOrTwoSubject(subjectService, oneSubjectName, "0");
            if (Objects.isNull(oneSubject)){
                oneSubject = new Subject();
                oneSubject.setTitle(oneSubjectName);
                oneSubject.setParentId("0");
                subjectService.save(oneSubject);
    
            }
            log.info("数据入库之后:{}", JSON.toJSONString(oneSubject));
    
            //判断数据库中有没有二级分类,要判断这个二级分类的话必须先获取此时的父id,这里有个很神奇的地方,mybatisplus在执行save(T obj)方法完成之后,
            // 就会将生成的主键赋值给obj中
            //oneSubject.getId()可以获取当前二级分类对应的一级分类的id
            Subject twoSubject = getOneOrTwoSubject(subjectService, twoSubjectName, oneSubject.getId());
            if (Objects.isNull(twoSubject)){
                twoSubject = new Subject();
                twoSubject.setParentId(oneSubject.getId());
                twoSubject.setTitle(twoSubjectName);
                subjectService.save(twoSubject);
            }
    
        }
    
        /**
         * 根据课程名称和父id查询课程分类表中的数据
         * @param subjectService
         * @param name
         * @param pid
         * @return
         */
        private Subject getOneOrTwoSubject(SubjectService subjectService,String name,String pid){
            QueryWrapper<Subject> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("title",name);
            queryWrapper.eq("parent_id",pid);
            return subjectService.getOne(queryWrapper);
    
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    
        }
    }

    8.测试成功

     

     

  • 相关阅读:
    js的alert乱码问题
    (6)select语句
    (5)视图
    (4)索引
    (3)操作数据库
    (2)MySQL数据类型
    (1)MySQL概述
    RocketMQ安装使用
    uniapp打包h5
    面试必问 如何保证缓存与数据库的一致性
  • 原文地址:https://www.cnblogs.com/wyq1995/p/13764608.html
Copyright © 2011-2022 走看看