前言
最近在写项目的时候有一个需求,就是实现动态表头的导入,那时候我自己也不知道动态表头导入是什么,查询了大量的网站和资料,终于了解了动态表头导入是什么。
一、准备工作
确保项目中引入了处理 Excel 文件的相关库,如EasyExcel
(从示例代码推测可能使用了类似的库,这里以EasyExcel
为例说明)。在项目的构建文件(如 Maven 的pom.xml
或 Gradle 的构建脚本)中添加相应的依赖配置,使其能够在项目中正常使用该库来读取和处理 Excel 文件。
例如在 Maven 项目中,添加类似如下的依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>具体版本号</version>
</dependency>
数据库准备:
确保数据库已经创建并且相关表结构已经定义好,以接收从 Excel 文件中导入的数据。表结构应该与要导入的 Excel 数据的格式和内容相匹配,包括列名和数据类型等。例如,如果要导入课程相关信息,数据库中应该有对应的课程表,包含如课程 ID、课程名称、授课教师等相关字段。
二、代码功能及流程
接口层(Controller):
/importExcel
接口:
/**
* excel批量导入到数据库
* */
@GetMapping("/importExcel")
public int importExcel(@RequestParam String filePath) {
// 此接口接收一个文件路径作为参数
// 作用是调用服务层(Service)的importExcel方法来实现Excel文件数据的导入
// 并返回导入操作的结果,结果可能表示成功导入的记录数或者导入是否成功的标识(如示例中返回0或其他非0值)
return sfCourseService.importExcel(filePath);
}
这个接口的主要功能是对外提供一个入口,让外部可以通过发送 HTTP GET 请求,并传递 Excel 文件的路径参数,来触发数据导入的操作。它本身并不进行实际的数据处理,而是将请求转发到服务层进行后续处理。
服务层(Service):
importExcel
方法(接口定义):
/**
* 根据课程id查找课程的详细信息
*
* @param filePath
* @return 1/0
*/
public int importExcel(String filePath);
这里定义了服务层的importExcel
方法的接口,它接收一个表示 Excel 文件路径的字符串参数,并且约定返回一个整数值来表示导入操作的结果(可能是成功导入的记录数或者简单的成功 / 失败标识,如示例中的 0 或其他非 0 值)。
三、importExcel
方法(实现):
public int importExcel(String filePath) {
// 首先创建一个ExcelImportUtilOne的实例,用于处理Excel文件的导入相关操作
ExcelImportUtilOne excelImportUtil = new ExcelImportUtilOne(filePath);
if(excelImportUtil.getResult()==0)
return 0;
// 如果ExcelImportUtilOne实例获取的结果为0,表示可能在读取Excel文件等前期操作中有问题,直接返回0表示导入失败
// 批量插入操作
sfCourseMapper.batchInsert(excelImportUtil.getDataList(),excelImportUtil.getColumns());
// 调用数据访问层(Mapper)的batchInsert方法,将从Excel文件中获取并处理好的数据列表以及列信息传递过去,实现数据的批量插入到数据库中
return excelImportUtil.getDataList().size();
// 最后返回成功导入的数据记录数,作为本次导入操作的结果反馈
}
这个方法在服务层实现了真正的数据导入逻辑。它先通过创建ExcelImportUtilOne
实例来处理 Excel 文件的读取和数据预处理,然后根据处理结果判断是否继续进行数据插入操作。如果前期处理没问题,就调用数据访问层的batchInsert
方法将数据插入数据库,并返回成功导入的记录数。
工具类(ExcelImportUtil 和 ExcelImportUtilOne):
ExcelImportUtil
类:
package cn.best.scholarflow.framework.basicfunctions.common;
import java.util.*;
public class ExcelImportUtil {
private String filePath;
private List<Map<Integer, String>> data;
private Map<Integer, String> header;
public ExcelImportUtil() {
// 默认构造函数
}
/**
* 从指定路径读取Excel文件并初始化数据和表头
*
* @param filePath 文件绝对路径
* @return 如果没有数据,则返回0;否则返回1
*/
public int importExcel(String filePath) {
this.filePath = filePath;
this.data = EasyExcelUtil.syncRead(filePath, 0, 0);
// 使用EasyExcelUtil的syncRead方法从指定路径读取Excel文件,读取的起始行和列都为0,将读取到的数据存储在data列表中
if (data.isEmpty()) {
return 0; // 如果没有数据,则直接返回
}
this.header = data.get(0);
// 如果读取到了数据,将第一行数据作为表头信息存储在header映射中
return 1;
}
/**
* 获取列名数组(不包括最后一列)
*
* @return 列名数组
*/
public String[] getColumns() {
if (header == null || header.isEmpty()) {
throw new IllegalStateException("Header is not initialized. Please call importExcel first.");
}
return header.values().stream().limit(header.size() - 1).toArray(String[]::new);
// 通过对流处理header映射的值,获取除最后一列之外的列名数组,用于后续数据处理和插入操作等
}
/**
* 转换数据格式
*
* @return 转换后的数据列表
*/
public List<Map<String, String>> convertToMapList() {
if (data == null || data.isEmpty() || header == null || header.isEmpty()) {
throw new IllegalStateException("Data or Header is not initialized. Please call importExcel first.");
}
List<Map<String, String>> dataList = new ArrayList<>();
for (int i = 1; i < data.size(); i++) { // 从第二行开始,因为第一行是表头
Map<Integer, String> row = data.get(i);
Map<String, String> map = new LinkedHashMap<>();
for (Integer key : header.keySet()) {
if (row.containsKey(key)) {
map.put(header.get(key), row.get(key));
}
}
dataList.add(map);
}
return dataList;
// 将读取到的原始数据格式(以行和列索引存储的数据)转换为以列名和对应值存储的映射列表形式,方便后续的数据处理和插入到数据库等操作
}
}
这个类主要负责从指定路径读取 Excel 文件,并对读取到的数据进行初步处理。它通过importExcel
方法实现文件读取,根据读取结果初始化表头和数据信息,然后提供了获取列名数组和转换数据格式的方法,为后续的数据插入操作做准备。
ExcelImportUtilOne
类:
package cn.best.scholarflow.framework.basicfunctions.common;
import cn.best.scholarflow.framework.basicfunctions.common.ExcelImportUtil;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Setter
@Getter
public class ExcelImportUtilOne {
private String[] columns;
private List<Map<String, String>> dataList;
private int result=1;
public ExcelImportUtilOne(String filePath) {
ExcelImportUtil excelImportUtil = new ExcelImportUtil();
int result = excelImportUtil.importExcel(filePath);
if (result == 0) {
this.result = 0;
}
// 首先创建一个ExcelImportUtil的实例,并调用其importExcel方法来读取和处理Excel文件,根据返回结果设置自身的result属性,表示读取操作的成功与否
// 获取表头信息
this.columns = excelImportUtil.getColumns();
this.dataList = excelImportUtil.convertToMapList();
// 获取处理后的列名数组和转换后的数据列表,以便后续在其他地方(如服务层的数据插入操作)使用
}
}
ExcelImportUtilOne
类是在ExcelImportUtil
类的基础上进行了进一步的封装。它在构造函数中调用ExcelImportUtil
的方法来处理 Excel 文件的读取和初步处理,根据处理结果设置自身的相关属性,包括列信息、数据列表以及表示整体操作结果的result
属性,为服务层等调用方提供了更方便的接口来获取处理好的数据和相关信息,以便进行后续的数据插入等操作。
四、动态表头导入的关键步骤及处理
读取 Excel 文件并获取表头信息(ExcelImportUtil
类):
在ExcelImportUtil
的importExcel
方法中,通过EasyExcelUtil.syncRead
方法从指定路径读取 Excel 文件,将读取到的数据存储在data
列表中。
如果data
列表不为空,将第一行数据(即表头信息)提取出来存储在header
映射中。这样就获取到了 Excel 文件的原始表头信息,后续可以根据这些表头信息来动态处理数据插入等操作。
处理表头信息以适应数据库插入(ExcelImportUtil
类):
通过getColumns
方法,对获取到的表头信息进行处理,将其转换为列名数组(不包括最后一列,根据具体需求可能有不同的处理方式,这里假设最后一列不需要作为数据库插入的列)。
这个列名数组将用于在数据插入数据库时,与数据库表的列名进行匹配,确保数据能够正确插入到对应的列中。
转换数据格式以便插入数据库(ExcelImportUtil
类):
在convertToMapList
方法中,从第二行开始遍历读取到的数据(因为第一行是表头已经单独处理了),将每一行的数据根据表头信息转换为以列名和对应值存储的映射列表形式。
这样处理后的数据列表(dataList
)就可以方便地与数据库表的结构进行匹配,在后续的数据插入操作中,能够按照列名准确地将数据插入到数据库相应的列中。
根据处理结果进行数据插入(Service
层的importExcel
方法):
在服务层的importExcel
方法中,首先创建ExcelImportUtilOne
实例来获取处理好的 Excel 文件数据相关信息,包括列信息和数据列表等。
如果ExcelImportUtilOne
实例获取的result
为 0,表示在读取 Excel 文件等前期操作中有问题,直接返回 0 表示导入失败。
如果result
不为 0,则调用数据访问层(Mapper
)的batchInsert
方法,将ExcelImportUtilOne
实例提供的dataList
和columns
作为参数传递过去,实现数据的批量插入到数据库中。最后返回成功导入的数据记录数作为本次导入操作的结果反馈。
通过以上详细的工作流程,从准备工作到各个代码模块的功能及相互协作,实现了动态表头导入的功能,能够将 Excel 文件中的数据根据其表头信息准确地批量插入到数据库中。