Java实现Excel批量导入数据库
场景说明
在实际开发中,经常需要解析Excel数据来插入数据库,而且通常会有一些要求,比如:全部校验成功才入库、校验成功入库,校验失败返回提示(总数、成功数、失败数、失败每行明细、导出失败文件明细…)
数据库表
CREATE TABLE `forlan_student` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.3</version>
</dependency>
导入模板
public class ForlanStudentExcelModule {
@ExcelProperty(value = "姓名", index = 0)
private String name;
@ExcelProperty(value = "年龄", index = 1)
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
错误失败模板
@HeadStyle(horizontalAlignment = HorizontalAlignment.LEFT)
@ContentStyle(horizontalAlignment = HorizontalAlignment.LEFT)
public class ForlanStudentErrorExcelModule {
@ColumnWidth(20)
@ExcelProperty(value = "失败原因", index = 0)
private String excelOneLineErrorMsg;
@ColumnWidth(10)
@ExcelProperty(value = "姓名", index = 1)
private String name;
@ColumnWidth(10)
@ExcelProperty(value = "年龄", index = 2)
private Integer age;
public String getExcelOneLineErrorMsg() {
return excelOneLineErrorMsg;
}
public void setExcelOneLineErrorMsg(String excelOneLineErrorMsg) {
this.excelOneLineErrorMsg = excelOneLineErrorMsg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "ForlanStudentErrorExcelModule{" +
"excelOneLineErrorMsg='" + excelOneLineErrorMsg + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@RestController
public class ExcelController {
@Autowired
private ForlanStudentService forlanStudentService;
@RequestMapping("/excel/import")
public String importFromExcel(@RequestParam(value = "file") MultipartFile param) {
// 校验文件类型
String fileName = param.getOriginalFilename();
if (StringUtils.isEmpty(fileName) || !(fileName.endsWith(".xlsx") || fileName.endsWith(".xls"))) {
return "文件后缀需为.xlsx或.xls";
}
return forlanStudentService.doImport(param);
}
}
public interface ForlanStudentService {
String doImport(MultipartFile param);
}
a、主方法
@Override
public String doImport(MultipartFile param) {
String result = "导入成功";
try (InputStream inputStream = param.getInputStream()) {
// 解析Excel对象流转成需要的对象
List<ForlanStudent> forlanStudentList = processExcel(inputStream);
// 最终入库数据
List<ForlanStudent> insertData = new ArrayList<>();
// 校验数据,并填充符合的数据
List<ForlanStudentErrorExcelModule> forlanStudentErrorExcelModule = checkDataAndFill(forlanStudentList, insertData);
if (!CollectionUtils.isEmpty(forlanStudentErrorExcelModule)) {
// 要求全部校验通过的话,这里可以直接return
// 需要的话,转成JSON返回,好看些
result = forlanStudentErrorExcelModule.toString();
// 可以生成错误文件,返回错误文件路径
// result = generateExceptionFile(forlanStudentErrorExcelModule);
}
if(!CollectionUtils.isEmpty(insertData)){
// 数据入库,根据自己需要写
forlanStudentDao.insertBatch(insertData);
}
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
return result;
}
b、解析Excel数据转为List对象
private List<ForlanStudent> processExcel(InputStream inputStream) throws Exception {
List<ForlanStudent> forlanStudentList = new ArrayList<>();
Integer maxRows = 100;
// 导入模板表头
List<String> chineseHeader = Arrays.asList("姓名", "年龄");
// 0是表头
final int headerRows = 0;
try (Workbook workbook = WorkbookFactory.create(inputStream)) {
Sheet sheet = workbook.getSheetAt(0);
int totalRow = sheet.getLastRowNum();
if (totalRow == 0) {
throw new Exception("文件内容为空");
} else if (totalRow - headerRows > maxRows) {
throw new Exception(String.format("单次导入数据不能超过%s条", maxRows));
}
// 遍历每行
for (int rowIndex = 0; rowIndex <= totalRow; rowIndex++) {
Row currentRow = sheet.getRow(rowIndex);
if (currentRow == null) {
continue;
}
// 读取数据行
List<String> cellList = new ArrayList<>();
for (int columnIndex = 0; columnIndex <= 1; columnIndex++) {
Cell currentCell = currentRow.getCell(columnIndex);
cellList.add(formatCellValue(currentCell));
}
// 校验模板是否正确
if (rowIndex <= headerRows) {
if (rowIndex == 0 && !cellList.equals(chineseHeader)) {
throw new Exception("文件模板错误");
}
continue;
}
if (null != cellList && !cellList.isEmpty()) {
ForlanStudent forlanStudent = new ForlanStudent();
forlanStudent.setName(cellList.get(0));
forlanStudent