资讯动态

Excel批量生成SQL脚本工具:POI+JDBC实现数据迁移提效

发布时间:2026/10/18 12:41:48 来源:尧图企业网站定制
简介这是一份面向Java开发者与数据库运维人员的数据处理工具源码包用于将Excel表格数据自动解析并生成可执行的SQL脚本再通过JDBC连接写入目标数据库适用于数据迁移、批量导入与数据分析等场景。压缩包共56个文件约24.48MB包含21个jar依赖、20个class与11个java源码另有classpath配置、project工程文件、fatjar打包产物及一份xls测试数据模板结构完整便于直接运行与二次开发。资源核心围绕Apache POI解析Excel、构造INSERT等SQL语句、配置数据库驱动并管理连接执行源码可帮助读者理解从表格字段映射到语句生成、再到JDBC执行与异常处理的完整链路。目前已有979人学习下载适合希望掌握Excel到数据库自动化转换思路的开发者参考借鉴。1. 从手工拼 SQL 到批量生成一个被低估的提效工具做数据迁移或者后台初始化的时候最烦的不是写 SQL而是把 Excel 里几百上千行配置数据一行行拼成 INSERT 语句。字段一多引号、逗号、NULL 值、日期格式随便哪个地方手滑就是一条报错。更别提产品经理隔三差五丢过来一个「补充数据.xlsx」你还得重新对一遍字段顺序。我最近拆的一个小工具就是干这件事的读取 Excel按模板生成可直接执行的 SQL 脚本同时支持通过 JDBC 连接数据库做校验或直接写入。它解决的核心问题很具体——把「人肉拼 SQL」变成「配置一次、批量产出」。适合谁做数据初始化、配置表导入、测试数据构造的后端和运维同学尤其是那种表结构固定、数据来源是 Excel 的场景。下面按「它怎么工作 → 怎么跑起来 → 坑在哪 → 怎么用得更顺」的顺序拆开讲。2. 解析 Excel 生成 SQL 的底层逻辑POI、JDBC 与模板三件套2.1 为什么是 POI JDBC 这套组合Excel 解析这块Java 生态里最稳的还是 Apache POI。它同时支持.xlsHSSF和.xlsxXSSF能拿到单元格的类型、格式、公式结果这对生成 SQL 很关键——因为你要判断一个格子到底是数字、字符串还是日期不能一律当字符串处理。JDBC 在这里有两个用途一是生成脚本前先连库校验表结构字段名、类型对不对二是小批量数据可以直接走PreparedStatement写入省去生成脚本再手动执行的步骤。常见做法是大批量走「生成 .sql 文件 命令行执行」小批量或需要即时反馈的走 JDBC 直连。模板引擎的选择上简单场景用字符串拼接加占位符就够了复杂场景多表、条件字段可以上 FreeMarker 或 Velocity。我一般建议先用最朴素的String.format或StringBuilder因为 SQL 生成的可读性比「优雅」重要——出问题时你得能一眼看出哪条语句拼错了。2.2 一次完整的生成流程拆解整个流程可以拆成五步每一步都有对应的代码落点读取 Excel 文件定位 sheet 和表头行逐行读取单元格按列映射到字段对每个值做类型转换和转义重点字符串单引号、日期格式、NULL套用 INSERT 模板生成 SQL 语句输出到文件或通过 JDBC 执行先看读取和映射的核心代码// 读取 Excel 并映射为字段-值对 public ListMapString, String readExcel(String filePath, int headerRow) throws IOException { ListMapString, String rows new ArrayList(); try (InputStream is new FileInputStream(filePath); Workbook workbook WorkbookFactory.create(is)) { // 自动识别 xls/xlsx Sheet sheet workbook.getSheetAt(0); Row header sheet.getRow(headerRow); ListString columns new ArrayList(); for (Cell cell : header) { columns.add(cell.getStringCellValue().trim()); // 表头即字段名 } // 从表头下一行开始读数据 for (int i headerRow 1; i sheet.getLastRowNum(); i) { Row row sheet.getRow(i); if (row null) continue; MapString, String record new LinkedHashMap(); for (int j 0; j columns.size(); j) { Cell cell row.getCell(j); record.put(columns.get(j), getCellValue(cell)); // 统一转字符串 } rows.add(record); } } return rows; }这段代码的关键点WorkbookFactory.create能自动判断文件格式省去手动区分 HSSF 和 XSSF用LinkedHashMap保证字段顺序和 Excel 列顺序一致生成 SQL 时字段和值的顺序不会错位表头直接当字段名意味着 Excel 第一行必须写数据库真实字段名这是约定也是约束。getCellValue是容易翻车的地方单独拎出来// 单元格取值处理数字、日期、布尔、公式等类型 private String getCellValue(Cell cell) { if (cell null) return null; switch (cell.getCellType()) { case STRING: return cell.getStringCellValue().trim(); case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { // 日期统一转成 yyyy-MM-dd HH:mm:ss return new SimpleDateFormat(yyyy-MM-dd HH:mm:ss) .format(cell.getDateCellValue()); } // 避免 1.0 这种科学计数法尾巴 double d cell.getNumericCellValue(); if (d Math.floor(d)) return String.valueOf((long) d); return String.valueOf(d); case BOOLEAN: return String.valueOf(cell.getBooleanCellValue()); case FORMULA: return cell.getCellFormula(); // 或按需取计算结果 default: return null; } }参数说明DateUtil.isCellDateFormatted判断是否是日期格式这一步不能省否则日期会变成一串数字序列号数字处理里判断d Math.floor(d)是为了把100.0还原成100不然生成的 SQL 里 ID 字段会变成浮点数插入整型列直接报错。2.3 生成 SQL 时的转义与模板拿到字段-值对之后生成 INSERT 语句// 根据字段映射生成 INSERT 语句 public String buildInsert(String tableName, MapString, String record) { StringBuilder cols new StringBuilder(); StringBuilder vals new StringBuilder(); for (Map.EntryString, String e : record.entrySet()) { cols.append(e.getKey()).append(,); vals.append(toSqlValue(e.getValue())).append(,); } cols.setLength(cols.length() - 1); // 去掉末尾逗号 vals.setLength(vals.length() - 1); return String.format(INSERT INTO %s (%s) VALUES (%s);, tableName, cols, vals); } // 值转 SQL 字面量字符串加引号并转义null 直接写 NULL private String toSqlValue(String value) { if (value null || value.isEmpty()) return NULL; // 单引号转义防止 SQL 注入和语法错误 return value.replace(, ) ; }逻辑说明toSqlValue里把单引号替换成两个单引号这是 SQL 标准转义方式能避免「OBrien」这类数据把语句截断。空字符串按 NULL 处理是个策略选择——如果你的表不允许 NULL就得改成写这个要按实际表结构定。提示如果字段里有纯数字但数据库列是 varchar上面的写法会带上引号没问题但如果列是 int 而 Excel 里混进了空值NULL 是合法的空字符串就会报错。类型边界一定要提前对齐。3. 把工具跑起来环境、依赖与 JDBC 直连配置3.1 依赖引入与最小可运行工程Maven 项目里引入 POI 和数据库驱动即可。POI 建议用 5.x 版本对.xlsx支持更完整dependencies !-- Excel 解析 -- dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.3/version /dependency !-- JDBC 驱动按实际数据库替换 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependency /dependencies参数说明poi-ooxml已经传递依赖了poi核心包不用重复引驱动版本要和数据库服务端匹配MySQL 8 用com.mysql.cj.jdbc.Driver5.x 用com.mysql.jdbc.Driver写错会报ClassNotFoundException。3.2 JDBC 连接与批量写入如果选择直连写入而不是生成脚本核心是用PreparedStatement加批处理// JDBC 批量写入避免逐条提交 public void batchInsert(ListMapString, String rows) throws SQLException { String sql INSERT INTO config_table (id, name, create_time) VALUES (?, ?, ?); try (Connection conn DriverManager.getConnection(URL, USER, PWD); PreparedStatement ps conn.prepareStatement(sql)) { conn.setAutoCommit(false); // 关闭自动提交 for (MapString, String row : rows) { ps.setLong(1, Long.parseLong(row.get(id))); ps.setString(2, row.get(name)); ps.setTimestamp(3, Timestamp.valueOf(row.get(create_time))); ps.addBatch(); // 加入批次 } ps.executeBatch(); // 一次性提交 conn.commit(); } }逻辑说明setAutoCommit(false)配合executeBatch能把上千条插入合并成一次网络往返性能差距在数据量大时非常明显。addBatch只是攒着executeBatch才真正发送。注意Timestamp.valueOf要求字符串格式严格是yyyy-MM-dd HH:mm:ss和前面 Excel 日期转换的格式必须一致否则抛IllegalArgumentException。3.3 生成脚本 vs JDBC 直连怎么选维度生成 .sql 文件JDBC 直连写入适用数据量大批量、可分批执行中小批量、即时反馈可审查性高脚本可人工检查低直接进库回滚难度低脚本可留存高需事务控制环境依赖只需文件需网络和驱动典型场景生产初始化、交付脚本测试数据、临时导入我一般建议生产环境走生成脚本先人工过一遍再执行测试环境走 JDBC 直连图快。两者不是互斥的同一个工具里可以都实现用参数切换。4. 避坑与排查那些让脚本跑不通的细节4.1 日期变成 45000 这种数字现象Excel 里明明是2024-01-01生成的 SQL 里却是45292。原因POI 把日期存为数字序列号如果没调DateUtil.isCellDateFormatted判断就会按普通数字取值。解决在getCellValue里先判断日期格式用cell.getDateCellValue()取值再格式化。另外注意 Excel 单元格格式本身要是「日期」类型如果只是手动输入了看起来像日期的文本POI 会当字符串处理这种反而不会出问题。4.2 数字精度丢失或多了小数点现象ID 是10000000000000001生成后变成1.0E16或10000000000000000。原因Excel 对超过 15 位的数字本身就会丢精度POI 读出来是 double再转字符串就变形了。解决长数字如订单号、身份证号在 Excel 里必须存成文本格式或者读取时用DataFormatter按显示值取。这是 Excel 的锅不是工具的锅但排查时容易误判。4.3 表头字段名和数据库对不上现象生成的 SQL 执行报Unknown column xxx。原因Excel 表头写了中文名或别名和数据库真实字段名不一致。解决约定表头必须写真实字段名或者在工具里加一层字段映射配置Excel 列名 → 数据库字段名。我倾向于加映射表因为业务同学填 Excel 时更愿意写中文。4.4 空行和合并单元格导致错位现象生成的 SQL 里某些行字段值整体偏移或者多出空 INSERT。原因Excel 里有空行、合并单元格getLastRowNum会把空行也算进去getCell遇到合并单元格只有左上角有值。解决读取时跳过row null和所有单元格都为空的行合并单元格场景建议在 Excel 侧拆开工具侧处理合并单元格成本高且容易出错。4.5 批量执行时事务太大撑爆内存现象JDBC 批量写入几万条时 OOM 或超时。原因一次性addBatch太多或者没分批提交。解决每 500 到 1000 条执行一次executeBatch并commit然后清空批次。这个阈值按数据库和网络情况调没有万能值。5. 进阶技巧用 DataFormatter 兜底与生成前的结构校验5.1 用 DataFormatter 拿到「所见即所得」的值前面getCellValue是按类型手动转换遇到复杂格式百分比、货币、自定义格式还是会翻车。更稳的做法是用 POI 的DataFormatter它按单元格显示格式输出字符串// 用 DataFormatter 按显示格式取值兜底复杂格式 DataFormatter formatter new DataFormatter(); FormulaEvaluator evaluator workbook.getCreationHelper().createFormulaEvaluator(); for (int j 0; j columns.size(); j) { Cell cell row.getCell(j); // 第二个参数传 evaluator公式单元格也能取到计算结果 String value formatter.formatCellValue(cell, evaluator); record.put(columns.get(j), value.trim()); }逻辑说明formatCellValue会尊重单元格的数字格式日期就是日期字符串百分比就是50%不会出现序列号。传evaluator是为了让公式单元格返回计算结果而不是公式本身。代价是它输出的是「显示值」如果 Excel 列宽不够显示成####取出来也可能是异常值所以列宽要正常。我一般会把这个方法和手动转换结合先用DataFormatter取再对日期和长数字做二次校正。纯手动转换适合格式规整的数据DataFormatter适合格式五花八门的业务表格。5.2 生成前先校验表结构与其等 SQL 执行报错不如生成前先连库查一次表结构把 Excel 表头和数据库字段做比对// 查询目标表字段和 Excel 表头比对 public SetString getTableColumns(Connection conn, String tableName) throws SQLException { SetString columns new HashSet(); DatabaseMetaData meta conn.getMetaData(); try (ResultSet rs meta.getColumns(null, null, tableName, null)) { while (rs.next()) { columns.add(rs.getString(COLUMN_NAME).toLowerCase()); } } return columns; }拿到字段集合后遍历 Excel 表头凡是数据库里没有的字段直接报出来让人去改 Excel而不是生成一堆注定失败的 SQL。这一步能省掉大量「执行到一半报错、回滚、改、重来」的循环。5.3 一个我踩过的坑有次给一个配置表导数据Excel 里有个字段叫status值是1和0我按字符串处理生成了1和0结果数据库列是 tinyintMySQL 隐式转换居然没报错数据也进去了。但后来另一个字段amount是 decimalExcel 里填了100.50我按字符串生成100.50插入时精度被截成了100查了半天才发现是引号导致类型没对上。从那以后我每次生成脚本前都会强制走一遍「字段类型比对」把数据库列类型和 Excel 值的形态对一遍数字列不加引号字符串列加引号日期列用标准格式。这个习惯帮我省了至少三次线上排查。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑