SpringBoot集成EasyExcel的應用場景分析

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

到此這篇關於SpringBoot集成EasyExcel的應用場景的文章就介紹到這瞭,更多相關SpringBoot集成EasyExcel內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: