一、前言
二、项目需求
三、数据库表设计
四、代码实现和测试
五、总结
进入9月以来,一直忙于项目,特别是临近国庆节这半个月来,被临时拉入新的项目组,需求变化莫测,项目时间很是紧迫。我最开始并非专职Java开发,本职是大数据开发,奈何公司环境如此,也时常参与Java项目开发。很久没有认真写博客了,经历了整个忙碌的夏天,特别是9月以来,笔者有感于所经手的项目收获不小,有很多值得总结和反复推敲之处,故写下这几篇系列文章,在回顾和总结时,也希望本系列文章能够对大家有所帮助。如有不当之处,敬请指正!
本文为系列文章第一篇。
废话不多说,先上需求:
拿到需求清单后,仔细查看,结合团队项目会议内容,总结出第一步也是最基础最核心的一步就是将客户的Excel表格数据导入后台PG数据库指定表。结合我们的项目架构采用的是spring boot+Mybatis,其实用自己的话来说就是实现Spring boot读取Excel并存入PG数据库,此为后端需要实现的功能,需要提供给前端一个读取Excel表格的接口,并实现将数据导入数据库。
明确了后端需要干什么,那接下来就比较清晰了。
首选在测试库中设计所需的测试表,包含表名,表字段,关联字段等等。经过和其他开发协调沟通后,确定了测试表如下:
导入数据存储表SQL代码段:
-- ----------------------------
-- Table structure for zgq_user
-- ----------------------------
DROP TABLE IF EXISTS "public"."zgq_user";
CREATE TABLE "public"."zgq_user" (
"id" varchar(64) COLLATE "pg_catalog"."default" NOT NULL DEFAULT NULL,
"xm" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL,
"xb" varchar(10) COLLATE "pg_catalog"."default" DEFAULT NULL,
"sfzh" varchar(32) COLLATE "pg_catalog"."default" DEFAULT NULL,
"gx" varchar(32) COLLATE "pg_catalog"."default" DEFAULT NULL,
"dz" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL,
"c_name" varchar(20) COLLATE "pg_catalog"."default" DEFAULT NULL,
"ad_code" varchar(20) COLLATE "pg_catalog"."default" DEFAULT NULL,
"ad_name" varchar(20) COLLATE "pg_catalog"."default" DEFAULT NULL,
"update_by" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL,
"update_time" date DEFAULT NULL,
"bz" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL,
"huhao" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL,
"bgzt" varchar(10) COLLATE "pg_catalog"."default" DEFAULT NULL
)
;
COMMENT ON COLUMN "public"."zgq_user"."id" IS '主键';
COMMENT ON COLUMN "public"."zgq_user"."xm" IS '姓名';
COMMENT ON COLUMN "public"."zgq_user"."xb" IS '性别';
COMMENT ON COLUMN "public"."zgq_user"."sfzh" IS '身份证号';
COMMENT ON COLUMN "public"."zgq_user"."gx" IS '关系';
COMMENT ON COLUMN "public"."zgq_user"."dz" IS '地址';
COMMENT ON COLUMN "public"."zgq_user"."c_name" IS '所属村';
COMMENT ON COLUMN "public"."zgq_user"."ad_code" IS '行政区划编码';
COMMENT ON COLUMN "public"."zgq_user"."ad_name" IS '行政区划名称';
COMMENT ON COLUMN "public"."zgq_user"."update_by" IS '操作人';
COMMENT ON COLUMN "public"."zgq_user"."update_time" IS '操作时间';
COMMENT ON COLUMN "public"."zgq_user"."bz" IS '备注';
COMMENT ON COLUMN "public"."zgq_user"."huhao" IS '户号';
COMMENT ON COLUMN "public"."zgq_user"."bgzt" IS '当前变更状态:默认未空';
COMMENT ON TABLE "public"."zgq_user" IS '资格权-用户记录表';
-- ----------------------------
-- Primary Key structure for table zgq_user
-- ----------------------------
ALTER TABLE "public"."zgq_user" ADD CONSTRAINT "zgq_user_pkey" PRIMARY KEY ("id");
实现spring boot读取Excel数据并存入PG数据库,将其拆解,第一是加载或者上传Excel表格;第二读取Excel数据;第三是将数据写入PG数据表中。
本次代码其实并非完全笔者亲自写的,而且借用了同事之前写好的基础上改进的。
controller层:
package com.fw.hs.controller;
import com.fw.hs.vo.ExcelImportVo;
import com.fw.hs.service.ExcelService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* @author
* @date 2020/9/8 10:09
*/
@Api(tags = "excel接口")
@RestController
@AllArgsConstructor
@RequestMapping("/api/excel")
public class ExcelController {
@Autowired
private ExcelService excelService;
@ApiOperation(value = "解析excel导入数据", notes = "其他备注")
@PostMapping("/import")
public boolean importData(@RequestParam(name="file",required = false) MultipartFile file, ExcelImportVo vo)
throws IllegalAccessException, IOException, InstantiationException {
excelService.importData(file, vo.getTableName(), vo.getColumns());
return true;
}
}
service层:
package com.fw.hs.service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
/**
* @author
* @date 2020/9/8 10:26
*/
public interface ExcelService {
void importData(MultipartFile file, String tableName, List columns) throws IOException, InstantiationException, IllegalAccessException;
}
Excel工具类:
package com.fw.hs.utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author
* @date 2020/9/8 10:47
*/
public class ExcelUtil {
public static List> readExcelFile(MultipartFile file, int length) throws IllegalAccessException, InstantiationException, IOException {
List> res = new ArrayList<>();
String fileName = file.getOriginalFilename();
Sheet sheet = null;
if (StringUtils.isNotBlank(fileName)) {
String suffix = fileName.substring(fileName.indexOf("."));
if ("xls".equals(suffix)) {
HSSFWorkbook wb = new HSSFWorkbook(file.getInputStream());
sheet = wb.getSheetAt(0);
} else {
XSSFWorkbook wb = new XSSFWorkbook(file.getInputStream());
sheet = wb.getSheetAt(0);
}
}
if (sheet == null) {
return res;
}
for (int j = 1;j < sheet.getPhysicalNumberOfRows();j++){
List list = new ArrayList<>();
Row row = sheet.getRow(j);
if (null != row){
for (int i = 0; i < length; i++) {
Cell cell = row.getCell(i);
if (cell != null) {
row.getCell(i).setCellType(CellType.STRING);
list.add(row.getCell(i).getStringCellValue().replaceAll(" ", ""));
}
}
}
res.add(list);
}
return res;
}
}
service实现类:
package com.fw.hs.service.impl;
import com.fw.hs.utils.ExcelUtil;
import com.fwcloud.common.base.support.IdGenerator;
import com.fw.hs.mapper.ExcelMapper;
import com.fw.hs.service.ExcelService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
/**
* @author
* @date 2020/9/8 10:26
*/
@Service
@Slf4j
public class ExcelServiceImpl implements ExcelService {
@Autowired
private ExcelMapper excelMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void importData(MultipartFile file, String tableName, List columns) throws IOException, InstantiationException, IllegalAccessException {
if (CollectionUtils.isEmpty(columns)) {
// 获取数据库字段名
columns = excelMapper.getTableColumns(tableName);
}
log.info("表:{},字段:{} ", tableName, columns);
List> lists = ExcelUtil.readExcelFile(file, columns.size());
for (List list: lists) {
if (CollectionUtils.isNotEmpty(list)) {
list.add(0, String.valueOf(IdGenerator.nextId()));
log.info("表:{} 导入数据:{}", tableName, list);
excelMapper.insert(tableName, columns, list);
}
}
}
}
Mybatis配置:
insert into ${tableName}
${column}
#{value}
测试数据格式:
测试接口:
依次填入Excel对应的字段,一个不多,一个不少。点击选择读取文件,输入导入数据存储表zgq_user表名。然后运行后天进行测试,如图:
由于项目紧急,需要在一周左右出一个版本,团队leader指定在原有接口基础上改,而接手时又没有任何资料或文档。虽然本次接口开发实现了基本的数据导入功能,但是考虑到具体业务和客户需求变更,总结出以下几点: