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 生成。这种方法不仅高效,而且易于扩展,适用于多种场景。

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


相关推荐
tryCbest3 天前
数据库SQL学习
数据库·sql
LAM LAB3 天前
【VBA】Excel指定单元格范围内字体设置样式,处理导出课表单元格
excel·vba
cowboy2583 天前
mysql5.7及以下版本查询所有后代值(包括本身)
数据库·sql
努力的lpp3 天前
SQL 报错注入
数据库·sql·web安全·网络安全·sql注入
麦聪聊数据3 天前
统一 Web SQL 平台如何收编企业内部的“野生数据看板”?
数据库·sql·低代码·微服务·架构
山峰哥3 天前
吃透 SQL 优化:告别慢查询,解锁数据库高性能
服务器·数据库·sql·oracle·性能优化·编辑器
在这habit之下3 天前
Keepalived学习总结
excel
轩情吖3 天前
MySQL初识
android·数据库·sql·mysql·adb·存储引擎
james的分享3 天前
大数据领域核心 SQL 优化框架Apache Calcite介绍
大数据·sql·apache·calcite
莫寒清3 天前
Apache Tika
java·人工智能·spring·apache·知识图谱