【SpringBoot+Mybatis-plus+poi】实现校验导入excel(一)
- 【SpringBoot+Mybatis-plus+poi】实现校验导入excel(一)
【SpringBoot+Mybatis-plus+poi】实现校验导入excel(一)
实现校验导入excel,我打算用两章实现:
第一章根据工厂模式(三个工厂:导入工厂、校验工厂和实现类导入方法工厂)和简单的非空校验实现user.xlsx的导入校验;
第二章根据工厂模式和自定义注解实现user.xlsx的导入校验。
首先看一下excel文档:
这里我删掉了部分单元格数据,作为非空校验的导入模板;
校验成功则根据id查询数据库的数据,如果结果为空则新增,否则更新;
校验失败则给对应单元格标红,并在最后一列打印错误信息。
上代码:
1. 实体(为Mybatis的Generator生成工具生成)
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@TableName("t_user")
public class TUser {
private Integer id;
private String name;
private String age;
private String sex;
private String address;
private String contactInformation;
}
2. dao层(为Mybatis的Generator生成工具生成)
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.import1.domain.TUser;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author wp
* @since 2022-01-04
*/
@Mapper
public interface TUserMapper extends BaseMapper<TUser> {
}
3. service层(为Mybatis的Generator生成工具生成)
3.1. api
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.import1.domain.TUser;
/**
* <p>
* 服务类
* </p>
*
* @author wp
* @since 2022-01-04
*/
public interface IUserService extends IService<TUser> {
}
3.1. impl(内部方法为实现类导入方法工厂的方法)
/**
* <p>
* 服务实现类
* </p>
*
* @author wp
* @since 2022-01-04
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements IUserService, ICombineService {
@Autowired
private TUserMapper userMapper;
@Override
@Transactional
public XSSFWorkbook importExcel(InputStream inputStream) throws IOException {
StringBuilder errorString = new StringBuilder();
XSSFWorkbook wb = new XSSFWorkbook(inputStream);
Sheet s = wb.getSheetAt(0);
int allNums = s.getPhysicalNumberOfRows();
IImport check = new UserImport(userMapper);
//校验
check.checkResult(errorString,wb,s,allNums,inputStream);
//导入数据
if (StringUtils.isBlank(errorString)){
check.createData(s, allNums);
wb = null;
}
return wb;
}
}
4. 实现类工厂生产实现类导入方法
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.IOException;
import java.io.InputStream;
/**
* 禽流感环境病原学监测信息Service接口
*
* @author wp
* @date 2021-12-17
*/
public interface ICombineService {
/**
* 通用导入
*/
XSSFWorkbook importExcel(InputStream inputStream) throws IOException;
}
5. mapper层
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.import1.mapper.TUserMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.example.import1.domain.TUser">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="age" property="age" />
<result column="sex" property="sex" />
<result column="address" property="address" />
<result column="contactInformation" property="contactInformation" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, name, age, sex, address, contactInformation
</sql>
</mapper>
6. 导入工厂
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.IOException;
import java.io.InputStream;
/**
* 抽象导入
* @author wp
*/
public interface IImport {
/**
* 校验
* @param errorMsg
* @param wb
* @param s
* @param allNums
* @param inputStream
* @throws IOException
*/
void checkResult(StringBuilder errorMsg, XSSFWorkbook wb, Sheet s, int allNums, InputStream inputStream)throws IOException;
/**
* 导入数据
* @param s
* @param allNums
*/
void createData(Sheet s, int allNums);
/**
* 导入数据处理
* @param s
* @param i
*/
void create(Sheet s, int i);
}
6.1. 生产导入
import com.example.import1.dispose.ICheck;
import com.example.import1.dispose.IImport;
import com.example.import1.dispose.check.UserCheck;
import com.example.import1.domain.TUser;
import com.example.import1.mapper.TUserMapper;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFDataFormat;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* user导入
* @author wp
*/
public class UserImport implements IImport {
private final DataFormatter DATAFORMATTER = new DataFormatter();
//excel起始校验行下标
private static final int START_INDEX = 1;
//excel错误信息下标
private static final int ERROR_INDEX = 6;
private final TUserMapper userMapper;
public UserImport(TUserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public void checkResult(StringBuilder errorMsg, XSSFWorkbook wb, Sheet s, int allNums, InputStream inputStream) throws IOException {
//获取默认单元格样式(在第一行创建一个空单元格获取默认样式)
XSSFCellStyle orgSty = (XSSFCellStyle) s.getRow(0).createCell(100).getCellStyle();
//设置单元格为文本格式
XSSFDataFormat format = wb.createDataFormat();
orgSty.setDataFormat(format.getFormat("@"));
//错误标红单元格样式
XSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
cellStyle.setDataFormat(format.getFormat("@"));
// 重复数据缓存
List<String> dataList = new ArrayList<>();
ICheck<TUserMapper> check = new UserCheck();
check.check(s, allNums, errorMsg, cellStyle, dataList, START_INDEX, ERROR_INDEX, DATAFORMATTER, userMapper);
}
@Override
public void createData(Sheet s, int allNums) {
for (int i = START_INDEX; i < allNums; i++) {
Row r = s.getRow(i);
if (r == null) {
continue;
}
// 导入禽流感环境病原学监测信息
create(s, i);
}
}
@Transactional
public void create(Sheet s, int i) {
Row r = s.getRow(i);
TUser user = new TUser();
// id
String id = DATAFORMATTER.formatCellValue(r.getCell(0));
int idInt = Integer.parseInt(id);
TUser userCondition = userMapper.selectById(id);
user.setId(idInt);
// 姓名
String name = DATAFORMATTER.formatCellValue(r.getCell(1));
user.setName(name);
// 年龄
String age = DATAFORMATTER.formatCellValue(r.getCell(2));
user.setAge(age);
// 性别
String sex = DATAFORMATTER.formatCellValue(r.getCell(3));
user.setSex(sex);
// 地址
String address = DATAFORMATTER.formatCellValue(r.getCell(4));
user.setAddress(address);
// 联系方式
String contactInformation = DATAFORMATTER.formatCellValue(r.getCell(5));
user.setContactInformation(contactInformation);
if (null != userCondition) {
userMapper.updateById(user);
}else {
userMapper.insert(user);
}
}
}
7. 校验工厂
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import java.util.List;
/**
* 抽象通用校验
* @param <M> mapper泛型
*/
public interface ICheck<M> {
/**
* 通用校验
* @param s
* @param allNums
* @param errorString
* @param cellStyle
* @param dataList
*/
void check(Sheet s, int allNums, StringBuilder errorString, XSSFCellStyle cellStyle, List<String> dataList,
int START_INDEX, int ERROR_INDEX, DataFormatter DATAFORMATTER, M m);
}
7.1. 生产校验方式
import com.example.import1.dispose.ICheck;
import com.example.import1.mapper.TUserMapper;
import com.example.import1.util.CheckoutUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import java.util.List;
/**
* user校验
*/
public class UserCheck implements ICheck<TUserMapper> {
@Override
public void check(Sheet s, int allNums, StringBuilder errorString, XSSFCellStyle cellStyle, List<String> dataList,
int START_INDEX, int ERROR_INDEX, DataFormatter dataFormatter, TUserMapper userMapper) {
for(int i = START_INDEX; i<allNums ;i++) {
//定义错误信息msg
StringBuilder errorMsg = new StringBuilder();
Row r = s.getRow(i);
if(r == null) {
break;
}
//获取错误信息清空
Cell orgErrCell = r.getCell(ERROR_INDEX);
if(orgErrCell!=null) {
orgErrCell.setCellValue("");
}
Cell idCell = r.getCell(0);
String id = dataFormatter.formatCellValue(idCell);
// 非空校验
CheckoutUtils.isNull("id", errorMsg, idCell, id, cellStyle, r, 0);
// 姓名
Cell nameCell = r.getCell(1);
String name = dataFormatter.formatCellValue(nameCell);
// 非空校验
CheckoutUtils.isNull("姓名", errorMsg, nameCell, name, cellStyle, r, 1);
// 年龄
Cell ageCell = r.getCell(2);
String age = dataFormatter.formatCellValue(ageCell);
// 非空校验
CheckoutUtils.isNull("年龄", errorMsg, ageCell, age, cellStyle, r, 2);
// 性别
Cell sexCell = r.getCell(3);
String sex = dataFormatter.formatCellValue(sexCell);
// 非空校验
CheckoutUtils.isNull("性别", errorMsg, sexCell, sex, cellStyle, r, 3);
// 地址
Cell addressCell = r.getCell(4);
String address = dataFormatter.formatCellValue(addressCell);
// 非空校验
CheckoutUtils.isNull("地址", errorMsg, addressCell, address, cellStyle, r, 4);
// 联系方式
Cell contactInformationCell= r.getCell(5);
String contactInformation = dataFormatter.formatCellValue(contactInformationCell);
// 非空校验
CheckoutUtils.isNull("联系方式", errorMsg, contactInformationCell, contactInformation, cellStyle, r, 5);
if(orgErrCell==null) {
orgErrCell= r.createCell(ERROR_INDEX);
}
orgErrCell.setCellValue(errorMsg.toString());
errorString.append(errorMsg);
}
}
}
8. 工具类
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
public class CheckoutUtils {
/**
* 非空校验
* @param zhName
* @param errorMsg
* @param cell
* @param cellStyle
* @return
*/
public static boolean isNull(String zhName, StringBuilder errorMsg, Cell cell, String cellString, XSSFCellStyle cellStyle, Row r, int i){
//如果为空则增加错误信息
if(StringUtils.isBlank(cellString)) {
errorMsg.append(zhName).append("必填; ");
setCellStyle(r, i, cell, cellStyle);
return false;
}
CellStyle style = cell.getCellStyle();
if (IndexedColors.RED.getIndex() == style.getFillBackgroundColor()) {
cell.setCellStyle(null);
}
return true;
}
private static void setCellStyle(Row r, int i, Cell cell, XSSFCellStyle cellStyle) {
if (null == cell) {
cell = r.createCell(i);
}
cell.setCellStyle(cellStyle);
}
}
9. pom路径
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>import1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>import1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
10. controller层
import com.example.import1.domain.TUser;
import com.example.import1.service.ICombineService;
import com.example.import1.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author wp
* @since 2022-01-04
*/
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
private IUserService userService;
@Autowired
private ICombineService combineService;
/**
* 用户查询
* @return
*/
@GetMapping("/query/user")
public ResponseEntity<List<TUser>> queryUser(){
return ResponseEntity.ok(userService.list());
}
/**
* 用户导入
*/
@PostMapping("/import/user")
public ResponseEntity<Boolean> importExcel(@RequestParam("file") MultipartFile file, HttpServletResponse response) throws IOException {
XSSFWorkbook wb = combineService.importExcel(file.getInputStream());
if (wb!=null){
ServletOutputStream outputStream = response.getOutputStream();
wb.write(outputStream);
wb.close();
outputStream.close();
}
return ResponseEntity.ok(true);
}
}
11. Generator代码生成工具
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.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
import java.util.List;
public class MpBatisGenerator {
private static String projectPath = null;
public static void main(String[] args) {
// projectPath = System.getProperty("user.dir");
projectPath = "D:\\本地项目\\import1";
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(configGlobalConfig());
autoGenerator.setDataSource(configDataSource());
autoGenerator.setStrategy(configStrategyConfig());
autoGenerator.setPackageInfo(configPackageConfig());
// autoGenerator.setCfg(customerConfig());
autoGenerator.execute();
}
/**
* 自定义生成文件配置
*
* @return
*/
// private static InjectionConfig customerConfig() {
// InjectionConfig config = new InjectionConfig() {
// @Override
// public void initMap() {
//
// }
// };
// List<FileOutConfig> files = new ArrayList<FileOutConfig>();
// files.add(new FileOutConfig() {
// @Override
// public String outputFile(TableInfo tableInfo) {
// String expand = "c:/dam";
// String entityFile = String.format((expand + File.separator + "%s" + ".java"), tableInfo.getControllerName());
// return entityFile;
// }
// });
// config.setFileOutConfigList(files);
// return config;
// }
/**
* 全局配置
*
* @return
*/
private static GlobalConfig configGlobalConfig() {
GlobalConfig config = new GlobalConfig();
// 是否支持AR模式
config.setActiveRecord(true)
// 作者
.setAuthor("wp")
// 生成路径
.setOutputDir(projectPath + "/src/test/java/")
// .setOutputDir("c:/pms/src/main/java/")
// 文件覆盖
.setFileOverride(true)
// 主键策略
.setIdType(IdType.AUTO)
// 设置生成的service接口的名字的首字母是否为I,例如IEmployeeService
.setServiceName("%sService")
//开启swagger2模式
.setSwagger2(true)
//生成基本的resultMap
.setBaseResultMap(true)
//生成基本的SQL片段
.setBaseColumnList(true)
//生成后打开文件夹
.setOpen(false).setDateType(DateType.ONLY_DATE);
return config;
}
/**
* 数据源配置
*
* @return
*/
private static DataSourceConfig configDataSource() {
DataSourceConfig dsConfig = new DataSourceConfig();
// 设置数据库类型
dsConfig.setDbType(DbType.MYSQL)
.setDriverName("com.mysql.cj.jdbc.Driver")
.setUrl("jdbc:mysql://localhost:3306/demo?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8")
.setUsername("root")
.setPassword("root")
.setTypeConvert(new MySqlTypeConvert() {
// 自定义数据库表字段类型转换【可选】
@Override
public IColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
System.out.println("转换类型:" + fieldType);
if (fieldType.toLowerCase().contains("tinyint")) {
return DbColumnType.INTEGER;
}
return super.processTypeConvert(globalConfig, fieldType);
}
});
return dsConfig;
}
/**
* 策略配置
*
* @return
*/
private static StrategyConfig configStrategyConfig() {
StrategyConfig stConfig = new StrategyConfig();
List<TableFill> tableFillList = new ArrayList<TableFill>();
//如 每张表都有一个创建时间、修改时间
//而且这基本上就是通用的了,新增时,创建时间和修改时间同时修改
//修改时,修改时间会修改,
//虽然像Mysql数据库有自动更新几只,但像ORACLE的数据库就没有了,
//使用公共字段填充功能,就可以实现,自动按场景更新了。
//如下是配置
/*TableFill sysCreateTime = new TableFill("create_time", FieldFill.INSERT);
TableFill sysUpdateTime = new TableFill("update_time", FieldFill.UPDATE);
TableFill sysCreateBy = new TableFill("create_by", FieldFill.INSERT);
TableFill sysUpdateBy = new TableFill("update_by", FieldFill.UPDATE);
tableFillList.add(sysCreateTime);
tableFillList.add(sysUpdateTime);
tableFillList.add(sysCreateBy);
tableFillList.add(sysUpdateBy);*/
// 全局大写命名
stConfig.setCapitalMode(true)
// 指定表名 字段名是否使用下划线
//.setDbColumnUnderline(true)
// 数据库表映射到实体的命名策略
.setNaming(NamingStrategy.underline_to_camel)
//.setTablePrefix("tbl_")
// 生成的表
.setInclude(new String[]{
"t_user"
})
.setEntityBooleanColumnRemoveIsPrefix(false)
// 自定义实体,公共字段
.setTableFillList(tableFillList);
return stConfig;
}
/**
* 包名策略配置
* @return
*/
private static PackageConfig configPackageConfig() {
PackageConfig pkConfig = new PackageConfig();
pkConfig.setParent("com.example.import1")
//dao
.setMapper("dao")
//service
.setService("service")
//controller
.setController("controller")
.setEntity("entity")
//mapper.xml
.setXml("mapper");
return pkConfig;
}
}
12. 配置文件yml
server:
port:
8546
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/demo
username: root
password: root
# MyBatis配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.example.import1.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
logging:
level:
com.example: debug
13. 项目层级
14. postman运行方式
15. 运行结果(校验失败)
16. 运行结果(补上缺失数据,校验成功)
本章对于SpringBoot+Mybatis-plus+poi实现导入校验就完成了,当我们有几个不同类型的单元格导入的情况下,可以实现实现类工厂导入方法,使用校验工厂和导入工厂分别生产对应类型的校验方法和导入方法,以完成对新增其他类型excel的导入校验。
下一章将本章的非空校验改为自定义注解+反射实现对excel表格校验。
纯手打,技术有限,请各位大佬批评指正,谢谢!