Excel 数据导入与 SQL 生成:基于 Hutool 和 Apache POI 的优雅实践

Excel 数据导入与 SQL 生成:基于 Hutool 和 Apache POI 的优雅实践

在日常开发中,我们经常会遇到需要从 Excel 文件中读取数据并生成 SQL 语句的场景。例如,批量更新员工邮箱、同步用户信息等任务。本文将结合一段示例代码,向大家展示如何利用 Hutool 工具库Apache POI 高效地实现这一功能。

如果你对以下内容感兴趣,这篇文章一定会让你有所收获:

  • 如何使用 Apache POI 解析 Excel 文件?
  • 如何通过反射机制动态创建 Java 对象?
  • 如何生成 SQL 更新语句,并确保代码简洁高效?

需求背景

假设我们需要从一个 Excel 文件中读取员工的昵称和公司邮箱信息,并生成对应的 SQL 更新语句,用于更新数据库中的 sys_user 表。最终目标是实现自动化处理,减少人工干预,提高效率。


解决方案概览

我们的解决方案分为以下几个步骤:

  1. 使用 Apache POI 解析 Excel 文件。
  2. 借助 Java 反射机制 将 Excel 中的数据映射到 Java 对象。
  3. 利用 Hutool 工具库 提供的工具方法(如 ObjectUtilStrUtil)进行数据校验和格式化。
  4. 根据解析结果生成 SQL 更新语句。

下面我们将逐步拆解代码逻辑,并分享一些实用的技巧。


代码详解
1. 准备工作:引入依赖

在项目中,我们需要引入以下 Maven 依赖:

xml 复制代码
<!-- 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 对象列表。以下是关键代码片段:

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 语句。以下是核心代码:

java 复制代码
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 方法:

java 复制代码
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 "";
    }
}

完整代码
java 复制代码
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 "";
        }
    }
}
总结

通过本文的示例代码,我们展示了如何利用 HutoolApache POI 实现 Excel 数据的解析与 SQL 生成。这种方法不仅高效,而且易于扩展,适用于多种场景。

如果您觉得这篇文章对您有所帮助,请点赞、收藏并分享给更多朋友!也欢迎在评论区留言,一起探讨更优雅的解决方案!


相关推荐
东方巴黎~Sunsiny2 小时前
EasyExcel导出excel再转PDF转图片详解
pdf·excel
Winter_world3 小时前
Excel中批量对多个结构相同的工作表执行操作,可以使用VBA宏来实现
excel·excel批量sheet操作
慧一居士3 小时前
EasyExcel集成使用总结与完整示例
java·excel
零凌林3 小时前
使用exceljs将excel文件转化为html预览最佳实践(完整源码)
前端·html·excel·vue3·最佳实践·文件预览·exceljs
lqlj22335 小时前
Spark SQL 读取 CSV 文件,并将数据写入 MySQL 数据库
数据库·sql·spark
遗憾皆是温柔6 小时前
MyBatis—动态 SQL
java·数据库·ide·sql·mybatis
晨曦backend6 小时前
EXCEL下拉菜单与交替上色设置
excel
未来之窗软件服务6 小时前
Cacti 未经身份验证SQL注入漏洞
android·数据库·sql·服务器安全
_星辰大海乀8 小时前
表的设计、聚合函数
java·数据结构·数据库·sql·mysql·数据库开发
tebukaopu14814 小时前
官方 Elasticsearch SQL NLPChina Elasticsearch SQL
大数据·sql·elasticsearch