一、課程分類管理需求分析
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進行測試:
上傳文件,點擊執行
數據庫中也可以看到信息加入成功