Excel转SQL工具:POI+JDBC实现数据迁移与批量插入
简介这是一份面向Java开发者与数据库运维人员的数据处理工具源码包用于将Excel表格数据自动解析并生成可执行的SQL脚本再通过JDBC连接写入目标数据库适用于数据迁移、批量导入与数据分析等场景。压缩包共56个文件约24.48MB以21个jar依赖、20个class编译文件、11个java源码为主另含classpath配置、xls测试模板与fatjar打包文件结构完整便于直接运行或二次开发。资源核心围绕Apache POI解析Excel、构造INSERT等SQL语句、配置数据库驱动并执行连接操作展开源码可帮助读者理解从工作簿读取、字段映射到语句执行的完整链路。目前已有979人学习下载适合希望掌握Excel到数据库自动化转换思路的开发者参考借鉴。1. 从手工导数到脚本直出这个工具到底解决什么问题做后台系统迁移或者数据初始化的时候最烦的往往不是写 SQL而是从 Excel 里一条条抠数据再拼 INSERT。产品给过来一张表几十列、几百行字段名和数据库列名还对不上手工拼到第三十条就开始眼花。这个项目就是冲着这个场景来的读一个 Excel 文件按配置好的映射关系自动生成可执行的 SQL 脚本顺带还能通过 JDBC 直连数据库把数据灌进去。它适合两类人——一类是经常做数据迁移、初始化脚本的后端开发另一类是测试同学需要批量造数据但不想手写 SQL。核心链路就三步解析 Excel、字段映射、生成或执行 SQL下面把它拆开讲清楚。2. 技术选型与整体架构为什么是 POI JDBC 这套组合2.1 技术栈拆解这个项目的技术栈不复杂属于典型的 Java 工具类应用选型逻辑也很务实。层次技术作用Excel 解析Apache POI读取 xls/xlsx兼容两种格式数据库连接JDBC直连执行 SQL不依赖 ORM配置读取Properties / YAML存字段映射和连接信息构建工具Maven依赖管理和打包日志SLF4J Logback记录解析和执行的每一步为什么不用 EasyExcel如果只是读EasyExcel 内存占用确实更小但这个项目需要拿到单元格的原始类型日期、数字、公式结果POI 的 Cell 对象控制粒度更细改起来也直观。为什么不用 MyBatis 批量插入因为项目的定位是「生成脚本」而不是「做业务写入」JDBC 的addBatch已经够用还少一层配置负担。2.2 整体架构与数据流整个项目可以拆成四个模块数据从 Excel 流到数据库中间经过映射层转换。Excel 文件 │ ▼ [ExcelReader] ── 读取行转成 ListMapString,Object │ ▼ [FieldMapper] ── 按配置把 Excel 列名映射成数据库列名 │ ▼ [SqlBuilder] ── 拼装 INSERT / UPDATE 语句 │ ├──► 输出 .sql 文件 │ ▼ [JdbcExecutor] ── 批量执行事务控制 │ ▼ 目标数据库这个分层的好处是每一层都能单独替换。比如你不想用 POI 了只改 ExcelReader不想生成 INSERT 想生成 UPDATE只改 SqlBuilder。模块之间通过ListMapString,Object这种朴素结构传递数据没有引入额外的 DTO减少理解成本。2.3 目录结构一个典型的工程目录长这样excel2sql/ ├── pom.xml ├── src/main/java/com/example/excel2sql/ │ ├── Main.java # 入口解析命令行参数 │ ├── reader/ │ │ └── ExcelReader.java # POI 读取封装 │ ├── mapper/ │ │ └── FieldMapper.java # 字段映射 │ ├── builder/ │ │ └── SqlBuilder.java # SQL 拼装 │ ├── executor/ │ │ └── JdbcExecutor.java # JDBC 执行 │ └── config/ │ └── AppConfig.java # 配置加载 └── src/main/resources/ ├── mapping.properties # 字段映射配置 └── db.properties # 数据库连接配置Main只做参数解析和流程编排真正的逻辑都在各自模块里。这种结构对二次开发很友好你想加一个「导出 CSV」的功能新建一个exporter包就行不用动现有代码。3. 核心模块拆解从读单元格到拼出 INSERT 语句3.1 ExcelReader把一行行单元格变成 MapPOI 读取的核心是处理单元格类型这是最容易翻车的地方——日期读成数字、数字读成科学计数法都是血泪经验。public ListMapString, Object read(String filePath, int sheetIndex, int headerRow) { ListMapString, Object rows new ArrayList(); try (Workbook workbook WorkbookFactory.create(new File(filePath))) { Sheet sheet workbook.getSheetAt(sheetIndex); Row header sheet.getRow(headerRow); // 先读表头作为 Map 的 key ListString columns new ArrayList(); for (Cell cell : header) { columns.add(getCellValue(cell).toString().trim()); } // 从表头下一行开始读数据 for (int i headerRow 1; i sheet.getLastRowNum(); i) { Row row sheet.getRow(i); if (row null) continue; MapString, Object map new LinkedHashMap(); for (int j 0; j columns.size(); j) { Cell cell row.getCell(j); map.put(columns.get(j), getCellValue(cell)); } rows.add(map); } } catch (Exception e) { log.error(读取 Excel 失败: {}, filePath, e); throw new RuntimeException(Excel 解析异常, e); } return rows; }WorkbookFactory.create会自动识别 xls 和 xlsx不用手动判断。headerRow参数指定表头在第几行因为有些 Excel 前面有标题行。用LinkedHashMap是为了保持列顺序后面拼 SQL 时字段顺序和 Excel 一致方便排查。getCellValue是类型转换的关键单独抽出来private Object getCellValue(Cell cell) { if (cell null) return null; switch (cell.getCellType()) { case STRING: return cell.getStringCellValue().trim(); case NUMERIC: // 日期类型必须单独判断否则会读成 45000 这种数字 if (DateUtil.isCellDateFormatted(cell)) { return new java.sql.Timestamp(cell.getDateCellValue().getTime()); } double d cell.getNumericCellValue(); // 整数去掉小数点避免 1 读成 1.0 if (d Math.floor(d) !Double.isInfinite(d)) { return (long) d; } return d; case BOOLEAN: return cell.getBooleanCellValue(); case FORMULA: return cell.getCellFormula(); default: return null; } }DateUtil.isCellDateFormatted这行是后悔药不加的话日期列会变成一串数字。整数判断是为了让id这种列生成1而不是1.0否则插入 int 列时数据库可能报类型不匹配。3.2 FieldMapperExcel 列名和数据库列名的翻译官Excel 里的表头往往是中文或者业务名数据库列名是英文下划线风格中间需要一层映射。配置写在mapping.properties里# Excel列名数据库列名 用户编号user_id 用户姓名user_name 手机号码phone 注册时间create_time映射逻辑本身很简单但要做容错public MapString, Object map(MapString, Object rawRow) { MapString, Object mapped new LinkedHashMap(); for (Map.EntryString, Object entry : rawRow.entrySet()) { String excelCol entry.getKey(); String dbCol mapping.getProperty(excelCol); if (dbCol null) { // 没有配置映射的列默认转小写下划线或者直接跳过 log.warn(列 [{}] 未配置映射已跳过, excelCol); continue; } mapped.put(dbCol, entry.getValue()); } return mapped; }这里有个设计取舍未配置的列是跳过还是报错我一般选择跳过加 warn 日志因为 Excel 里经常有备注列、说明列强制要求全配置反而累赘。但如果你的场景要求严格把continue改成throw就行。3.3 SqlBuilder拼 INSERT 时怎么防注入和转义拼 SQL 最怕两件事字符串里的单引号没转义导致语法错误以及 null 值处理不当。看核心代码public String buildInsert(String tableName, MapString, Object row) { StringBuilder cols new StringBuilder(); StringBuilder vals new StringBuilder(); for (Map.EntryString, Object entry : row.entrySet()) { cols.append(entry.getKey()).append(,); vals.append(toSqlValue(entry.getValue())).append(,); } // 去掉末尾逗号 cols.setLength(cols.length() - 1); vals.setLength(vals.length() - 1); return String.format(INSERT INTO %s (%s) VALUES (%s);, tableName, cols, vals); } private String toSqlValue(Object value) { if (value null) { return NULL; } if (value instanceof Number || value instanceof Boolean) { return value.toString(); } if (value instanceof java.sql.Timestamp) { return value ; } // 字符串转义单引号和反斜杠 String s value.toString() .replace(\\, \\\\) .replace(, ); return s ; }toSqlValue是安全底线。数字和布尔直接输出不加引号时间戳加引号字符串先转义反斜杠再转义单引号顺序不能反否则\会被二次转义。这里没有用 PreparedStatement因为目标是生成静态 SQL 文件但如果你走 JDBC 执行路径建议在 Executor 层用PreparedStatement再包一层。3.4 JdbcExecutor批量执行和事务控制生成 SQL 文件是一种用法直连数据库执行是另一种。批量插入必须开事务和批处理否则几千行数据能跑到天荒地老。public int executeBatch(String url, String user, String password, ListString sqlList) { int total 0; try (Connection conn DriverManager.getConnection(url, user, password)) { conn.setAutoCommit(false); // 关闭自动提交 try (Statement stmt conn.createStatement()) { for (String sql : sqlList) { stmt.addBatch(sql); // 每 500 条执行一次避免批次过大 if (total % 500 0) { stmt.executeBatch(); stmt.clearBatch(); } } stmt.executeBatch(); // 执行剩余 conn.commit(); } catch (SQLException e) { conn.rollback(); // 出错整体回滚 throw e; } } catch (SQLException e) { log.error(批量执行失败, e); throw new RuntimeException(e); } return total; }setAutoCommit(false)和commit/rollback是标配。每 500 条执行一次是经验值太小网络往返多太大内存压力大。clearBatch别忘了否则批次会一直累积。4. 跑起来配置、命令行参数和两种执行模式4.1 环境准备和依赖JDK 8 以上Maven 3.6 以上。pom.xml里核心依赖就三个dependencies dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdorg.slf4j/groupId artifactIdslf4j-simple/artifactId version2.0.7/version /dependency /dependenciespoi-ooxml同时支持 xls 和 xlsx不用再单独引poi。数据库驱动按你实际用的换PostgreSQL 就换成postgresql驱动。4.2 配置文件怎么写db.propertiesjdbc.urljdbc:mysql://localhost:3306/test_db?useUnicodetruecharacterEncodingutf8 jdbc.userroot jdbc.passwordyour_password jdbc.tableuser_infomapping.properties就是前面说的字段映射。两个文件都放在resources下打包时会进 jar。4.3 两种运行模式模式一只生成 SQL 文件不连数据库。java -jar excel2sql.jar \ --file ./data/user.xlsx \ --sheet 0 \ --header 0 \ --mode file \ --output ./out/user.sql模式二直连数据库执行。java -jar excel2sql.jar \ --file ./data/user.xlsx \ --sheet 0 \ --header 0 \ --mode db参数说明--file是 Excel 路径--sheet是 sheet 索引从 0 开始--header是表头行号--mode选file或db--output只在 file 模式下需要。Main里用简单的循环解析args没有引第三方 CLI 库保持轻量。4.4 执行结果怎么看file 模式下打开生成的.sql文件检查三件事字段名是否和数据库一致、字符串是否正确转义、日期格式是否是yyyy-MM-dd HH:mm:ss。db 模式下看日志里的executeBatch输出成功会打印总条数失败会打印具体 SQL 和异常堆栈。5. 避坑与排查这几类问题我踩过不止一次5.1 日期列读出来是一串数字现象Excel 里明明是2024-01-01生成 SQL 变成45292。原因POI 把日期存成数字getNumericCellValue返回的是序列号没有走日期分支。解决在getCellValue里用DateUtil.isCellDateFormatted(cell)判断是日期就调getDateCellValue。如果 Excel 里日期是文本格式存的那读出来就是字符串不用特殊处理。5.2 中文乱码现象生成的 SQL 里中文变成???。原因读文件时没指定编码或者 JDBC URL 没加characterEncodingutf8。解决POI 读 Excel 本身不涉及编码问题乱码通常出在写 SQL 文件时。用Files.newBufferedWriter(path, StandardCharsets.UTF_8)明确指定 UTF-8。JDBC URL 里加上useUnicodetruecharacterEncodingutf8。5.3 批量执行到一半报主键冲突现象前 500 条成功第 501 条报Duplicate entry整个事务回滚。原因Excel 里有重复数据或者目标表已有相同主键。解决生成 SQL 时把INSERT INTO改成INSERT IGNORE INTOMySQL或ON CONFLICT DO NOTHINGPostgreSQL。或者在 Executor 里捕获单条异常记录失败行号后继续最后统一报告。我一般选后者因为能知道具体哪几行有问题。5.4 数字精度丢失现象Excel 里是123456789012345生成 SQL 变成1.23456789012345E14。原因POI 读大数字时用了 double超过 15 位有效数字就丢精度。解决在getCellValue的 NUMERIC 分支里如果数字很大用BigDecimal.valueOf(cell.getNumericCellValue()).toPlainString()转成字符串。更稳妥的做法是用DataFormatter它能按单元格显示格式输出。5.5 表头有空格或换行导致映射失败现象配置里写的是用户编号但 Excel 表头是用户编号末尾有空格映射不上。原因表头读取时没 trim。解决读表头时统一.trim()并且把全角空格也替换掉。配置文件的 key 也做同样处理两边对齐。6. 二次开发从单表到多表关联的改造思路这个项目目前是单表 INSERT但实际业务里经常遇到「Excel 一张表要拆到两张关联表」的情况。比如用户表和用户详情表Excel 里是平铺的入库要拆开。改造的核心思路是在 FieldMapper 和 SqlBuilder 之间加一层「表拆分器」。具体做法在mapping.properties里用前缀区分目标表。# user_info 表 user_info.user_id用户编号 user_info.user_name用户姓名 # user_detail 表 user_detail.user_id用户编号 user_detail.address地址 user_detail.email邮箱然后 FieldMapper 返回的不再是单个 Map而是MapString, MapString,Objectkey 是表名。SqlBuilder 遍历这个 Map为每张表生成独立的 INSERT。JdbcExecutor 里要注意执行顺序先插主表再插从表如果有外键约束还得把主表生成的主键回填到从表——这一步如果主键是自增的就需要用getGeneratedKeys拿回来。// 主表插入后拿自增主键 try (PreparedStatement ps conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) { ps.executeUpdate(); try (ResultSet rs ps.getGeneratedKeys()) { if (rs.next()) { long pk rs.getLong(1); // 把 pk 塞回从表的 Map 里 detailRow.put(user_id, pk); } } }另一个常见的改造点是加校验。比如手机号列必须符合格式、必填列不能为空。我一般会在 FieldMapper 之后加一个Validator链每个校验器实现同一个接口配置里声明要启用哪些。这样加规则不用改主流程。验证改造是否成功最直接的办法是拿一份包含边界数据的 Excel——空值、超长字符串、特殊字符、重复主键——跑一遍看生成的 SQL 能不能在测试库上完整执行。我现在的习惯是每次改完映射或拼装逻辑都强制走一遍这个边界用例集因为字符串转义和 null 处理这两个地方翻车概率实在太高。希望这些能帮到你。本文还有配套的精品资源点击获取

相关新闻

Notepad++ 安装包下载与配置全指南:编码、插件、正则避坑

Notepad++ 安装包下载与配置全指南:编码、插件、正则避坑

简介:Notepad安装包面向Windows平台下需要轻量级文本与代码编辑工具的程序员、运维人员及普通用户,用于替代系统自带记事本,解决日常文本处理、代码编写与调试中的效率问题。压缩包共104个文件,约3.9MB,以89个xml配置文…

2026/10/18 9:25:28 阅读更多 →
异次元发卡网源码部署全攻略:PHP版本、环境配置与避坑指南

异次元发卡网源码部署全攻略:PHP版本、环境配置与避坑指南

简介:这是一套面向发卡网搭建者与PHP开发者的异次元发卡网源码,采用原生PHP从零重构,数据库底层使用Eloquent ORM,模板渲染基于Smarty3.1与原生PHP混合,会话全程由session保持,适合想快速搭建虚拟商品自动发…

2026/10/18 9:25:28 阅读更多 →
从单体对话到多智能体协作:Agent、Skills、Teams三层架构演进实践

从单体对话到多智能体协作:Agent、Skills、Teams三层架构演进实践

如果要对齐主流的推理范式,单线程的口头指令在今天已经不太够用了。过去一年我带着团队把一套以单体对话为核心的系统,分三个阶段重构成了Agent、Skills、Teams三个层次分明的架构,这期间踩了很多坑,也亲手扔掉过不少看似先进的方…

2026/10/18 9:24:28 阅读更多 →

最新新闻

WebGL着色器入门:GLSL ES数据类型与GPU编程思维

WebGL着色器入门:GLSL ES数据类型与GPU编程思维

1. 这不是“学编程”,而是给GPU写一封精准的信你打开一个网页,看到旋转的3D模型、流动的粒子特效、实时渲染的滤镜——这些画面背后,真正干活的不是CPU,而是显卡里的GPU。而WebGL,就是浏览器里唯一能让你直接和GPU对话…

2026/10/18 19:58:37 阅读更多 →
deepin系统部署HDFS 3.1.3实战:JDK8适配与systemd服务配置

deepin系统部署HDFS 3.1.3实战:JDK8适配与systemd服务配置

1. 项目概述:为什么在deepin上跑HDFS不是“折腾”,而是真需求HDFS编程实践(基于deepin 20.1社区版&Hadoop 3.1.3)——这个标题里藏着三个关键信号:国产化操作系统适配、大数据底层存储的本地化开发闭环、以及一个被…

2026/10/18 19:58:37 阅读更多 →
React 19 位运算全解析:从 Lane 优先级到 Fiber 副作用标记

React 19 位运算全解析:从 Lane 优先级到 Fiber 副作用标记

1. 先搞清楚位运算在 React 里到底管什么读 React 19 源码的人迟早会撞上同一面墙:到处都是pendingLanes | lane、flags & Placement、flags & ~Mask这种"非人话"代码。我第一次看到getHighestPriorityLane的实现时愣了很久,因为整个函…

2026/10/18 19:58:37 阅读更多 →
Spark on YARN内存计算调优实战:堆内堆外与YARN容器的精准对齐

Spark on YARN内存计算调优实战:堆内堆外与YARN容器的精准对齐

1. 项目概述:为什么“SPARK ON YARN内存计算”不是一句空话,而是工程落地的硬门槛你打开某公司内部数据平台的监控看板,发现Spark作业的Executor GC时间突然飙升到每次3.8秒,Shuffle Write耗时翻了4倍,而YARN集群的Nod…

2026/10/18 19:58:37 阅读更多 →
大模型提示工程实战:从原理到落地的提示词设计指南

大模型提示工程实战:从原理到落地的提示词设计指南

今天想认真聊聊“大模型提示工程”这件事。做内容、写代码、跑数据的人,现在多多少少都在用大模型。但很多人会有一种感觉:别人用起来很顺手,自己用起来像跟一个喝了三杯咖啡的实习生对话——说了半天,对方交上来的东西总差一点意…

2026/10/18 19:58:37 阅读更多 →
YOLO钢表面缺陷检测数据集配置与训练避坑指南

YOLO钢表面缺陷检测数据集配置与训练避坑指南

简介:本资源是面向计算机视觉初学者与工业检测项目开发者的YOLO钢表面缺陷检测实战数据集,专为解决金属材料质检中目标检测模型训练难、标注格式不统一、环境配置复杂等痛点设计。资源包含10000张真实产线采集的高质量钢表面图像,配套VOC&…

2026/10/18 19:57:36 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →