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.測試成功

 

 

 

 

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