Mooc項目開發筆記(九):EasyExcel使用、課程分類管理接口後臺實現

一、課程分類管理需求分析

Mooc項目的課程分類採用二級分類,也就是分爲兩級,例如:

  • 後端
    • Java
    • C++
  • 機器學習
    • 傳統機器學習
    • CV
    • NLP

在數據庫中的存儲結構主要包含三個字段

  • id:唯一標識一個課程分類
  • name:分類名稱
  • parent_id:2級分類中父類的id,1級分類的parent_id爲0

另外,課程分類的信息不再採用表單的形式,而是通過導入excle添加

二、EasyExcel簡介

1、Excel導入導出的應用場景

1、數據導入:減輕錄入工作量

2、數據導出:統計信息歸檔

3、數據傳輸:異構系統之間數據傳輸

2、EasyExcel特點

  • Java領域解析、生成Excel比較有名的框架有Apache poi、jxl等。但他們都存在一個嚴重的問題就是非常的耗內存。如果你的系統併發量不大的話可能還行,但是一旦併發上來後一定會OOM或者JVM頻繁的full gc。
  • EasyExcel是阿里巴巴開源的一個excel處理框架,以使用簡單、節省內存著稱。EasyExcel能大大減少佔用內存的主要原因是在解析Excel時沒有將文件數據一次性全部加載到內存中,而是從磁盤上一行行讀取數據,逐個解析。
  • EasyExcel採用一行一行的解析模式,並將一行的解析結果以觀察者的模式通知處理(AnalysisEventListener)。

三、Excel寫

1、pom中引入xml相關依賴

在service_edu的pom下引入下面的依賴

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.1.1</version>
    </dependency>
</dependencies>

注意還需要引入poi的依賴,而且版本要和easyexcel對應service模塊已經引入過了,所以不需要放入service_edu中

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
        </dependency>

2、創建實體類

設置表頭和添加的數據字段

在service_edu的test目錄下創建cn/hanzhuang42/excel/DemoData.java實體類

package cn.hanzhuang42.excel;

import lombok.Data;

@Data
public class DemoData {

    //設置表頭名稱,和它對應的列
    @ExcelProperty(value = "學生編號", index = 0)
    private Integer sno;

    @ExcelProperty(value = "學生姓名", index = 1)
    private String sname;
}

3、實現寫操作

在test中進行測試:


public class TestEasyExcel {

    //實現寫入excle
    @Test
    public void writeTest(){

        //設置寫入文件夾地址和文件名稱
        String filename = "C:\\Users\\ada\\Desktop\\writeTest.xlsx";

        //調用方法實現寫操作
        //第一個參數是文件路徑名,第二個參數是實體類的class
        EasyExcel.write(filename, DemoData.class).sheet("學生列表").doWrite(getData());
    }

    private static List<DemoData> getData() {
        List<DemoData> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            DemoData d = new DemoData();
            d.setSno(i);
            d.setSname("Eva" + i);
            list.add(d);
        }
        return list;
    }
}

結果:

在這裏插入圖片描述

4、實現讀操作

1)創建對excel表中數據對應的實體類。這裏仍然使用上面的DemoData

2)創建讀取操作的監聽器

public class ExcelListener extends AnalysisEventListener<DemoData> {

    /**
     * 一行一行的讀出excel中的數據
     * @param demoData  每行的數據
     * @param analysisContext
     */
    @Override
    public void invoke(DemoData demoData, AnalysisContext analysisContext) {
        System.out.println("------"+demoData);
    }

    /**
     * 讀取表頭的內容
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表頭:" + headMap);
    }

    /**
     * 讀取完成後會調用
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    }
}

3、調用實現最終的讀取

    //實現excel的讀操作
    @Test
    public void readTest(){

        //設置讀取文件夾地址和文件名稱
        String filename = "C:\\Users\\ada\\Desktop\\writeTest.xlsx";

        //調用方法實現讀操作
        EasyExcel.read(filename, DemoData.class, new ExcelListener()).sheet().doRead();
    }

四、課程分類管理接口後端實現

1、service-edu模塊配置依賴

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.1.1</version>
    </dependency>
</dependencies>

上邊過程中已經引入

2、代碼生成器生成代碼

使用Mybatis-plus的代碼生成器生成代碼mapper,service,controller

參見開發筆記二

創建完成之後有幾個地方需要修改:

1)創建和修改時間上加上@TableField註解,用於自動填充時間

@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;

}

2)、Controller上加上@CrossOrigin用於允許跨域訪問

@RestController
@RequestMapping("/eduservice/subject")
@CrossOrigin
public class EduSubjectController {
	..
}

3、創建與Excel數據對應的實體類

創建包cn.hanzhuang42.eduservice.entity.excel,並在下面創建與excel數據對應的實體類:

package cn.hanzhuang42.eduservice.entity.excel;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

@Data
public class SubjectData {

    //設置表頭名稱,和它對應的列
    @ExcelProperty( index = 0)
    private String topSubjectName;

    @ExcelProperty(index = 1)
    private String subSubjectName;

}

4、創建讀取Excel監聽器

package cn.hanzhuang42.eduservice.listener;

import cn.hanzhuang42.eduservice.entity.EduSubject;
import cn.hanzhuang42.eduservice.entity.excel.SubjectData;
import cn.hanzhuang42.eduservice.service.EduSubjectService;
import cn.hanzhuang42.servicebase.exception.CustomException;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {

    //監聽器沒有交給spring管理,所以不能自動注入
    private EduSubjectService subjectService;

    public SubjectExcelListener() {
    }

    public SubjectExcelListener(EduSubjectService subjectService) {
        this.subjectService = subjectService;
    }

    /**
     * 讀取每行數據並存入數據庫
     *
     * @param subjectData
     * @param analysisContext
     */
    @Override
    public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
        if (subjectData == null) {
            throw new CustomException(20001, "文件內容爲空");
        }
        //這一行中的一級分類和二級分類的名稱
        String topSubjectName = subjectData.getTopSubjectName();
        String subSubjectName = subjectData.getSubSubjectName();

        //判讀該行的一級分類是否存在,不存在則插入
        EduSubject topSubject = existTopSubject(topSubjectName);
        if (topSubject == null) { //表中沒有相同的一級分類,需要插入
            topSubject = new EduSubject();
            topSubject.setTitle(topSubjectName);
            topSubject.setParentId("0");
            subjectService.save(topSubject);
        }

        //插入數據之後mp會自動將id放入topSubject中
        String parent_id = topSubject.getId();

        //判讀該行的一級分類是否存在,不存在則插入
        EduSubject subSubject = existSubSubject(subSubjectName, parent_id);
        if (subSubject == null) { //表中沒有相同的一級分類,需要插入
            subSubject = new EduSubject();
            subSubject.setTitle(subSubjectName);
            subSubject.setParentId(parent_id);
            subjectService.save(subSubject);
        }
    }

    //判斷一級分類是否存在
    private EduSubject existTopSubject(String name) {
        QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
        wrapper.eq("title", name)
                .eq("parent_id", 0);
        EduSubject subject = subjectService.getOne(wrapper);
        return subject;
    }

    //判斷二級分類是否存在
    private EduSubject existSubSubject(String name, String parent_id) {
        QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
        wrapper.eq("title", name)
                .eq("parent_id", parent_id);
        EduSubject subject = subjectService.getOne(wrapper);
        return subject;
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

5、修改service

修改service接口

package cn.hanzhuang42.eduservice.service;

import cn.hanzhuang42.eduservice.entity.EduSubject;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;

/**
 * <p>
 * 課程科目 服務類
 * </p>
 *
 * @author Miracle42
 * @since 2020-06-25
 */
public interface EduSubjectService extends IService<EduSubject> {

    void saveSubject(MultipartFile file, EduSubjectService subjectService);
}

修改sevice實現類

package cn.hanzhuang42.eduservice.service.impl;

import cn.hanzhuang42.eduservice.entity.EduSubject;
import cn.hanzhuang42.eduservice.entity.excel.SubjectData;
import cn.hanzhuang42.eduservice.listener.SubjectExcelListener;
import cn.hanzhuang42.eduservice.mapper.EduSubjectMapper;
import cn.hanzhuang42.eduservice.service.EduSubjectService;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

/**
 * <p>
 * 課程科目 服務實現類
 * </p>
 *
 * @author Miracle42
 * @since 2020-06-25
 */
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {

    /**
     * 添加課程分類
     * @param file
     * @param subjectService
     */
    @Override
    public void saveSubject(MultipartFile file, EduSubjectService subjectService) {
        try {
            InputStream inputStream = file.getInputStream();
            EasyExcel.read(inputStream, SubjectData.class, new SubjectExcelListener(subjectService)).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6、修改controller

package cn.hanzhuang42.eduservice.controller;


import cn.hanzhuang42.commonutils.R;
import cn.hanzhuang42.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 Miracle42
 * @since 2020-06-25
 */
@RestController
@RequestMapping("/eduservice/subject")
@CrossOrigin
public class EduSubjectController {

    @Autowired
    private EduSubjectService subjectService;

    /**
     * 添加課程分類
     * 通過前端上傳文件到服務器,
     * 然後通過easyexcel讀取並存入數據庫
     */
    @PostMapping("addSubject")
    public R addSubject(MultipartFile file){

        subjectService.saveSubject(file, subjectService);

        return R.ok();
    }
}

7、測試

打開swagger進行測試:

在這裏插入圖片描述

上傳文件,點擊執行

在這裏插入圖片描述

數據庫中也可以看到信息加入成功
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章