- 引入依赖
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
<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>
</dependencies>
- 创建与Excel表格对应的实体类 , 通过
@ExcelProperty(index = 0)
注解中的index指定字段与Excel表中的哪儿列相对应
package com.starcpdk.edu.eduservice.entity.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
@Data
public class SubjectData {
@ExcelProperty(index = 0)
private String oneSubjectName;
@ExcelProperty(index = 1)
private String twoSubjectName;
}
- 使用代码生成器快速构建service , controller , mapper层 , 具体使用方式参考这篇文章
package com.starcpdk.demo;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
/**
* @author
* @since 2018/12/13
*/
public class CodeGenerator {
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir("I:\\Projects\\IND_Projects\\cpdkedu\\Back_End\\cpdkedu\\cpdkedu_parent\\service\\service_edu" + "/src/main/java/");
gc.setAuthor("姚云峰");
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER_STR); //主键策略
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/cpdkedu?serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
//com.starcpdk.edu.eduservice
pc.setParent("com.starcpdk.edu");
pc.setModuleName("eduservice"); //模块名
//com.starcpdk.edu.eduservice.controller
pc.setController("controller");
//com.starcpdk.edu.eduservice.entity
pc.setEntity("entity");
//com.starcpdk.edu.eduservice.service
pc.setService("service");
//com.starcpdk.edu.eduservice.mapper
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("edu_subject");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
由于使用easyExcel读操作需要使用到监听器 , 监听器是在service层new出来的 , 所以监听器SubjectExcelListener
无法使用spring管理 , 因此无法使用@Autowired
自动装配注入service层对象进行数据库的增删改查操作 , 那么我们该怎么解决呢?
解决方式如下:
那么我们就通过从controller传递注入的service层对象subjectService
实现注入监听器中
controller层传递到service层 , service层new的监听器对象通过有参构造将subjectService
传递到监听器SubjectExcelListener
中.
- 编写controller内容 , 使用MultipartFile 上传文件 , 将上传的文件和service层的对象传到service层
package com.starcpdk.edu.eduservice.controller;
import com.starcpdk.edu.commonutils.R;
import com.starcpdk.edu.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 姚云峰
* @since 2020-07-01
*/
@RestController
@RequestMapping("/eduservice/subject")
@CrossOrigin
public class EduSubjectController {
@Autowired
private EduSubjectService subjectService;
//添加课程分类
//获取到上传的文件 , 把文件内容读取出来
@PostMapping("addSubject")
public R addSubject(MultipartFile file){
//上传过来的excel文件
subjectService.saveSubject(file , subjectService);
return R.ok();
}
}
- service层书写
saveSubject
方法 , 通过file.getInputStream();
方法获取文件的流 , 通过EasyExcel.read(in , SubjectData.class , new SubjectExcelListener(subjectService)).sheet().doRead();
方法实现读excel表中数据。具体的逻辑处理在监听器中进行
package com.starcpdk.edu.eduservice.service.impl;
import com.alibaba.excel.EasyExcel;
import com.starcpdk.edu.eduservice.entity.EduSubject;
import com.starcpdk.edu.eduservice.entity.excel.SubjectData;
import com.starcpdk.edu.eduservice.listener.SubjectExcelListener;
import com.starcpdk.edu.eduservice.mapper.EduSubjectMapper;
import com.starcpdk.edu.eduservice.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 姚云峰
* @since 2020-07-01
*/
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
//添加课程分类
@Override
public void saveSubject(MultipartFile file , EduSubjectService subjectService) {
try {
//文件输入流
InputStream in = file.getInputStream();
//调用方法进行读取
EasyExcel.read(in , SubjectData.class , new SubjectExcelListener(subjectService)).sheet().doRead();
}catch (Exception e){
e.printStackTrace();
}
}
}
- 书写监听器 , 比对excel表中的名字和级数是否存在表中进行筛选是否加入数据库 , 监听器是继承
AnalysisEventListener
类的 , 泛型是与excel表对应的实体类
package com.starcpdk.edu.eduservice.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.starcpdk.edu.eduservice.entity.EduSubject;
import com.starcpdk.edu.eduservice.entity.excel.SubjectData;
import com.starcpdk.edu.eduservice.service.EduSubjectService;
import com.starcpdk.edu.service_base.exceptionhandler.MyException;
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
//因为SubjectExcelListener不能交给spring进行管理 , 需要自考管理 , 不能注入其他对象
//不能用mapper实现数据库操作
public EduSubjectService subjectService;
public SubjectExcelListener() {}
public SubjectExcelListener(EduSubjectService subjectService) {
this.subjectService = subjectService;
}
//读取excel内容 , 一行一行读取
@Override
public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
if (subjectData == null){
throw new MyException(20001 , "文件数据为空");
}
//一行一行读取 , 每次读取有两个值 , 第一个值以及分类 , 第二个值为二级分类
//判断一级分类是否重复
EduSubject existOneSubject = this.existOneSubject(subjectService, subjectData.getOneSubjectName());
if (existOneSubject == null){//没有相同一级分类 , 进行添加
existOneSubject = new EduSubject();
existOneSubject.setParentId("0");
existOneSubject.setTitle(subjectData.getOneSubjectName());//一级分类名称
subjectService.save(existOneSubject);
}
//获取一级分类的id值
String pid = existOneSubject.getId();
//添加二级分类
//判断二级分类是否存在
EduSubject existTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid);
if (existTwoSubject == null){
existTwoSubject = new EduSubject();
existTwoSubject.setParentId(pid);
existTwoSubject.setTitle(subjectData.getTwoSubjectName());//二级分类名称
subjectService.save(existTwoSubject);
}
}
//判断一级分类不能重复添加
private EduSubject existOneSubject(EduSubjectService subjectService , String name){
QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
wrapper.eq("title" , name);
wrapper.eq("parent_id" , "0");
EduSubject oneSubject = subjectService.getOne(wrapper);
return oneSubject;
}
//判断二级分类不能重复添加
private EduSubject existTwoSubject(EduSubjectService subjectService , String name , String pid){
QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
wrapper.eq("title" , name);
wrapper.eq("parent_id" , pid);
EduSubject twoSubject = subjectService.getOne(wrapper);
return twoSubject;
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
具体思路:
Excel表中的数据分为两列 , 通过@ExcelProperty(index = 0)
中的index与excel表中的列数相对应 , 下边的代码则表示excel表中的第一列的数据会赋值给oneSubjectName
即以及分类中,同理二级分类也是如此
在数据库表中如何区分一级分类二级分类呢?如何让其想对应上呢?
通过id与parent_id两个字段所决定 , 一级分类的parent_id是0 , 二级分类的parent_id是一级分类相对应的id值
在监听器中通过读取excel表中数据 , 对应根据分类的名称和分类的级数去数据库查找数据 , 若查到 , 说明该分类在数据库中已存在 , 无需再次插入 , 若查询返回值为空 ,则将此条数据插入到数据库中
@ExcelProperty(index = 0)
private String oneSubjectName;
@ExcelProperty(index = 1)
private String twoSubjectName;