Excel 数据导入与 SQL 生成:基于 Hutool 和 Apache POI 的优雅实践
在日常开发中,我们经常会遇到需要从 Excel 文件中读取数据并生成 SQL 语句的场景。例如,批量更新员工邮箱、同步用户信息等任务。本文将结合一段示例代码,向大家展示如何利用 Hutool 工具库 和 Apache POI 高效地实现这一功能。
如果你对以下内容感兴趣,这篇文章一定会让你有所收获:
- 如何使用 Apache POI 解析 Excel 文件?
- 如何通过反射机制动态创建 Java 对象?
- 如何生成 SQL 更新语句,并确保代码简洁高效?
需求背景
假设我们需要从一个 Excel 文件中读取员工的昵称和公司邮箱信息,并生成对应的 SQL 更新语句,用于更新数据库中的 sys_user
表。最终目标是实现自动化处理,减少人工干预,提高效率。
解决方案概览
我们的解决方案分为以下几个步骤:
- 使用 Apache POI 解析 Excel 文件。
- 借助 Java 反射机制 将 Excel 中的数据映射到 Java 对象。
- 利用 Hutool 工具库 提供的工具方法(如
ObjectUtil
和StrUtil
)进行数据校验和格式化。 - 根据解析结果生成 SQL 更新语句。
下面我们将逐步拆解代码逻辑,并分享一些实用的技巧。
代码详解
1. 准备工作:引入依赖
在项目中,我们需要引入以下 Maven 依赖:
<!-- Hutool 工具库 -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.20</version>
</dependency><!-- Apache POI(支持 Excel 解析) -->
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version>
</dependency><!-- Spring Core(用于加载资源文件) -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.22</version>
</dependency>
2. 核心代码解读
(1) 解析 Excel 文件
我们使用 Apache POI 来读取 Excel 文件,并将其转换为 Java 对象列表。以下是关键代码片段:
public static <T> List<T> readExcelToBeans(String filePath, Class<T> clazz) {ClassPathResource classPathResource = new ClassPathResource(filePath);List<T> beans = new ArrayList<>();Workbook workbook = null;InputStream inputStream = null;try {// 获取输入流inputStream = classPathResource.getInputStream();// 根据文件后缀判断 Excel 类型if (filePath.endsWith("xlsx")) {workbook = new XSSFWorkbook(inputStream); // 支持 .xlsx 文件} else if (filePath.endsWith("xls")) {workbook = new HSSFWorkbook(inputStream); // 支持 .xls 文件} else {throw new IllegalArgumentException("文件不是有效的Excel文件");}// 获取第一个工作表Sheet sheet = workbook.getSheetAt(0);// 获取标题行,并构建字段索引映射Row headerRow = sheet.getRow(0);Map<String, Integer> columnMap = new HashMap<>();for (Cell cell : headerRow) {String columnName = cell.getStringCellValue().trim();columnMap.put(columnName, cell.getColumnIndex());}// 遍历数据行并创建对象for (int i = 1; i <= sheet.getLastRowNum(); i++) {Row row = sheet.getRow(i);if (row == null) continue; // 跳过空行T bean = clazz.getDeclaredConstructor().newInstance();for (Field field : clazz.getDeclaredFields()) {String fieldName = field.getName();if (columnMap.containsKey(fieldName)) {Cell cell = row.getCell(columnMap.get(fieldName));if (cell != null) {Object value = getCellValue(cell);field.setAccessible(true);field.set(bean, ObjectUtil.isNull(value) ? "" : value);}}}beans.add(bean);}} catch (Exception e) {e.printStackTrace();} finally {// 关闭资源try {if (inputStream != null) inputStream.close();if (workbook != null) workbook.close();} catch (IOException e) {e.printStackTrace();}}return beans;
}
亮点分析:
- 泛型支持:
readExcelToBeans
方法支持任意类型的 Java 对象,灵活性高。 - 字段映射:通过标题行和字段名的匹配,自动将单元格值赋值给对象属性。
- 异常处理:全面捕获可能的异常,确保程序稳定性。
(2) 生成 SQL 更新语句
在解析完 Excel 数据后,我们可以根据业务需求生成 SQL 语句。以下是核心代码:
for (HrmUserEmail hrmUserEmail : hrmUserEmailList) {if (ObjectUtil.isNotEmpty(hrmUserEmail.getCompanyEmail()) && ObjectUtil.isNotEmpty(hrmUserEmail.getNickname())) {String sql = "UPDATE sys_user SET `company_email`=\"" + StrUtil.trim(hrmUserEmail.getCompanyEmail()) + "\" WHERE `nick_name`=\"" + StrUtil.trim(hrmUserEmail.getNickname()) + "\";";System.out.println(sql);}
}
优化建议:
- SQL 注入防护:在生产环境中,建议使用
PreparedStatement
或 ORM 框架(如 MyBatis)来避免 SQL 注入风险。 - 日志记录:可以将生成的 SQL 语句写入日志文件,方便后续排查问题。
(3) 辅助方法:获取单元格值
为了兼容不同类型的单元格(字符串、数字、日期等),我们编写了一个通用的 getCellValue
方法:
private static Object getCellValue(Cell cell) {switch (cell.getCellType()) {case STRING:return cell.getStringCellValue();case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {return new SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue());} else {return String.format("%.0f", cell.getNumericCellValue());}case BOOLEAN:return cell.getBooleanCellValue();case FORMULA:return cell.getCellFormula();case BLANK:default:return "";}
}
完整代码
package com.example.demo.hrmChange;import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.example.demo.hrmChange.domain.HrmUserEmail;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class GenEmailSqlTest {public static void main(String[] args) throws IOException {try {List<HrmUserEmail> hrmUserEmailList = readExcelToBeans("template/员工邮箱.xlsx", HrmUserEmail.class);for (HrmUserEmail hrmUserEmail : hrmUserEmailList) {if (ObjectUtil.isNotEmpty(hrmUserEmail.getCompanyEmail()) && ObjectUtil.isNotEmpty(hrmUserEmail.getNickname())) {String sql = "UPDATE sys_user SET `company_email`=\"" + StrUtil.trim(hrmUserEmail.getCompanyEmail()) + "\" WHERE `nick_name`=\"" + StrUtil.trim(hrmUserEmail.getNickname()) + "\";";System.out.println(sql);}}} catch (Exception e) {e.printStackTrace();}}public static <T> List<T> readExcelToBeans(String filePath, Class<T> clazz) {ClassPathResource classPathResource = new ClassPathResource(filePath);List<T> beans = new ArrayList<>();Workbook workbook = null;InputStream inputStream = null;try {inputStream = classPathResource.getInputStream();if (filePath.endsWith("xlsx")) {workbook = new XSSFWorkbook(inputStream);} else if (filePath.endsWith("xls")) {workbook = new HSSFWorkbook(inputStream);} else {throw new IllegalArgumentException("文件不是有效的Excel文件");}Sheet sheet = workbook.getSheetAt(0);// 获取第一行作为标题行Row headerRow = sheet.getRow(0);Map<String, Integer> columnMap = new HashMap<>();for (Cell cell : headerRow) {String columnName = cell.getStringCellValue().trim();columnMap.put(columnName, cell.getColumnIndex());}// 遍历剩余行并创建Bean对象for (int i = 1; i <= sheet.getLastRowNum(); i++) {Row row = sheet.getRow(i);if (row == null) {continue; // 如果行为空,则跳过}T bean = clazz.getDeclaredConstructor().newInstance();for (Field field : clazz.getDeclaredFields()) {String fieldName = field.getName();if (columnMap.containsKey(fieldName)) {Cell cell = row.getCell(columnMap.get(fieldName));if (cell != null) {Object value = getCellValue(cell);field.setAccessible(true);field.set(bean, ObjectUtil.isNull(value) ? "" : value);}}}beans.add(bean);}} catch (IOException | IllegalAccessException | InstantiationException | NoSuchMethodException |InvocationTargetException e) {e.printStackTrace();} finally {try {if (inputStream != null) {inputStream.close();}if (workbook != null) {workbook.close();}} catch (IOException e) {e.printStackTrace();}}return beans;}private static Object getCellValue(Cell cell) {switch (cell.getCellType()) {case STRING:return cell.getStringCellValue();case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {return new SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue());} else {return String.format("%.0f", cell.getNumericCellValue());}case BOOLEAN:return cell.getBooleanCellValue();case FORMULA:return cell.getCellFormula();case BLANK:default:return "";}}
}
总结
通过本文的示例代码,我们展示了如何利用 Hutool 和 Apache POI 实现 Excel 数据的解析与 SQL 生成。这种方法不仅高效,而且易于扩展,适用于多种场景。
如果您觉得这篇文章对您有所帮助,请点赞、收藏并分享给更多朋友!也欢迎在评论区留言,一起探讨更优雅的解决方案!