☰
Excel数据导入MySQL:Java JDBC批处理与防重复更新实战
2026/10/19 6:31:26 网站建设 项目流程

简介:这是一份面向Java初学者的数据库交互实战项目,解决Excel表格与MySQL之间的双向数据同步问题。项目基于Apache POI完成xls/xlsx文件读取与生成,通过JDBC建立数据库连接,对已存在数据执行更新、新数据执行插入,并支持将数据库查询结果导出为Excel表格,完整覆盖文件解析、数据类型处理、SQL增删改查等核心环节。压缩包共20个文件,包含6个Java源码、6个编译后的class文件、2个依赖jar包(POI与MySQL驱动)、SQL建表脚本及Eclipse工程配置文件,整体仅1.31MB,小巧适合作课程设计或自学练手。当前已有1737人学习,资源包目录结构清晰,src目录存放全部源码,lib目录集中管理第三方依赖,javenforexcel.sql提供现成数据库初始化脚本,拿到后可直接导入项目运行,便于对照学习POI操作Excel与JDBC操作MySQL的完整流程。

1. 别小看这个导入工具:Excel 数据导入 MySQL 不只 INSERT 一条路

第一次跑这份 Java 工程,是因为我要把一张上千行的 Excel 数据导入 MySQL,而且表里已经有一部分相同主键的记录。如果只写一条 INSERT,第二次跑同样的文件就会报主键冲突,或者留下一堆重复行。这个压缩包里的东西正好解决这件事:用 Java 读 .xls 文件,通过 JDBC 连接 MySQL,库里已有相同记录就 UPDATE,没有才 INSERT,并且能把库里的数据反向导出成 Excel。工程本身不花哨,没有 Spring,没有前端页面,核心就是几个类加两个 jar 包,适合正在做课程设计、内部工具或者想了解 JDBC 和批处理怎么配合的开发者。下载解压后,改一个数据库密码,从 main 方法就能跑起来。

2. 先看清技术选型与目录结构:两个 jar 决定了这个工程能做什么

2.1 为什么是 jxl 而不是 POI:一个老但能跑的组合

压缩包里的 lib 目录只放了两个文件:mysql-connector-java-5.1.7-bin.jar 和 jxl.jar。这个细节很重要——它说明整个工程的 Excel 读写用的不是 POI 的 HSSFWorkbook,而是 jxl(Java Excel API)来解析 .xls 格式。很多讲解 Excel 导入的文章默认用 POI,因为 POI 同时支持 .xls 和 .xlsx,功能也全;但这个项目选择 jxl,图的是轻量:一个 jar 只有几百 KB,API 也简单,很少代码就能把一个工作表读成二维数组。缺点也很明显:jxl 只认 Excel 97-2003 的 .xls,拿 .xlsx 喂进去会直接抛异常。如果手头文件是 .xlsx,要么另存为 .xls,要么把解析层换成 POI 的 XSSFWorkbook。这一点在二次开发时要提前跟业务方确认,否则数据文件发过来才发现格式不对,白忙活。

数据库侧用的是 JDBC 标准接口,驱动是 mysql-connector-java-5.1.7-bin.jar。注意 5.1.7 是 5.1 系列里比较早的版本,对应 MySQL 5.x 的协议没有问题;如果你本机装的是 MySQL 8.0,这个驱动也能连,但连接串要额外加时区参数,否则会在建连接时报 Server time zone 相关错误。更推荐的做法是直接把 lib 下的驱动替换成 mysql-connector-java 8.0.x,同时把 Driver 类名从 com.mysql.jdbc.Driver 换成 com.mysql.cj.jdbc.Driver。这些坑放在后面避坑小节统一说,这里先记住:项目本身依赖极简,没有 Maven,也没有 Gradle,全部依赖靠 Eclipse 的 Build Path 指向 lib 目录。

数据库连接串在这个项目里是一个非常关键的参数。一种常见写法如下:

private static final String URL = "jdbc:mysql://localhost:3306/excel_import" + "?useUnicode=true&characterEncoding=utf8";

useUnicode=true的作用是告诉驱动在客户端和服务器之间使用 Unicode 传输,characterEncoding=utf8则指定具体的字符集。这两个参数不写,中文很容易变成问号。后面 5.1 会专门说这个坑。如果你换成了 MySQL 8 驱动,还需要追加serverTimezone=Asia/Shanghai,否则驱动不知道用什么时区去转换 DATETIME 类型。

2.2 目录结构:一个 Eclipse 工程在压缩包里长什么样

解压后是一个标准 Eclipse 项目,顶层文件如下:

java实现Excel数据导入到mysql数据库/ ├── .classpath # Eclipse 编译路径配置 ├── .project # Eclipse 项目描述 ├── .settings/ # 编码、JDK 编译级别等配置 ├── bin/ # 编译后的 .class 输出目录 ├── src/ # 源码目录 │ └── com/excel/ │ ├── DBHelper.java # 数据库连接工具 │ ├── ExcelImporter.java # Excel 导入主逻辑 │ ├── ExcelExporter.java # 导出 Excel 的对应逻辑 │ └── Main.java # 入口:先导入再导出 ├── lib/ │ ├── jxl.jar │ └── mysql-connector-java-5.1.7-bin.jar ├── SS.txt # 说明或运行笔记 └── javenforexcel.sql # 建库建表脚本

原压缩包里 src 下的包名可能是 com.javen,类名也不一定和我上面写的一致,但按职责去对应不会有问题。这里统一改成 com.excel 来演示,避免原始包名干扰理解。bin 目录里是编译好的 class 文件,如果你直接跑 bin 下的类,必须保证 lib 两个 jar 在当前 classpath 里,否则运行阶段照样报 ClassNotFoundException。

.settings 里的 org.eclipse.jdt.core.prefs 会记录源码级别和默认编码,常见配置是 UTF-8,但老工程有时写着 GBK,后面读中文乱码时优先查这里。.classpath 里会写每个源码目录和 jar 的路径,如果这个文件缺失或损坏,Eclipse 导入时会识别不了一个 Java 工程,只能当成普通文件夹打开。SS.txt 是一个容易被忽略的文件,里面通常是作者留下的运行说明、数据库账号或者某个控制台日志输出。不要删它,先打开看看有没有关键的连接信息。我见过不少同学下载工程后不看 SS.txt,直接跑源码找不到入口,翻了好几层包才发现线索写在一个普通文本里。

2.3 建表脚本:先定主键,再谈导入更新

javenforexcel.sql 是整套逻辑的基石。导入是 INSERT 还是 UPDATE,完全取决于表里有没有一条 id 相同的记录,所以这张表必须定义一个唯一键。脚本常见的形态类似:

CREATE DATABASE IF NOT EXISTS excel_import DEFAULT CHARACTER SET utf8mb4; USE excel_import; CREATE TABLE IF NOT EXISTS payment ( id VARCHAR(32) NOT NULL, name VARCHAR(64) DEFAULT NULL, amount DECIMAL(12,2) DEFAULT NULL, remark VARCHAR(255) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

我见过的业务表大多是订单、用户、流水这类有自然主键的表。这里用 VARCHAR 做主键,是为了和 Excel 里的 id 直接对应,避免把数字字段转为字符串时出现精度问题。amount 用 DECIMAL 而不是 DOUBLE,因为浮点会在金额上产生 19.99 显示成 19.989999 的玄学问题。如果你要把这套源码用到自己的表,重点不是照抄字段,而是先确认业务表的主键或唯一索引,再把它映射到 Excel 的某一列。

如果表里没有主键,代码里的 SELECT 判断就会失去依据:第一次导入 100 行成功,第二次同样的文件再导入,SELECT 每次都查不到“同一条记录”,于是全部再次 INSERT,数据翻倍。这是很多导入工具“跑两遍就乱”的根本原因。所以拿到脚本的第一件事,是看主键定义在哪一列。脚本执行完会得到一个空表,第一次导入全部走 INSERT,第二次再导入相同 id 的记录就会走 UPDATE,这正是项目更新策略的基础。建议在建表时把常用的业务字段一次性加好,否则后面扩展还得 ALTER TABLE。

3. 核心模块拆解:一条 Excel 记录从单元格到 MySQL 行的完整链路

3.1 读取 Excel:jxl 的 Sheet 和 Cell 怎么用

导入的第一步是把 .xls 文件拉进内存。原工程里 ExcelImporter 的一段核心代码,本质上就是下面这段 jxl 操作:

import jxl.Sheet; import jxl.Workbook; public List<Map<String, String>> readExcel(File file) throws Exception { List<Map<String, String>> rows = new ArrayList<>(); Workbook workbook = Workbook.getWorkbook(file); Sheet sheet = workbook.getSheet(0); // 第一个工作表 for (int i = 1; i < sheet.getRows(); i++) { // 第 0 行是表头,跳过 Map<String, String> row = new LinkedHashMap<>(); row.put("id", sheet.getCell(0, i).getContents().trim()); row.put("name", sheet.getCell(1, i).getContents().trim()); row.put("amount", sheet.getCell(2, i).getContents().trim()); rows.add(row); } workbook.close(); return rows; }

有几个容易踩的细节:getCell(column, row)的参数顺序是列在前、行在后,别写成getCell(i, 0)。getContents()返回的是 String,Excel 里的数字、日期都会被转成展示文本,因此后面写 SQL 时全部用setString也能配合 VARCHAR 主键。循环从 1 开始,是因为绝大多数表都有表头;如果源文件确实没有表头,可以改成从 0 开始,但首行数据要格外小心。LinkedHashMap能保证列顺序,调试打印行数据时不会乱。

这里不建议用getSheet("Sheet1")去按名字取表,因为不同同事生成的 Excel 工作表名可能是中文或其他,按下标取第一个表最稳。如果业务上一个文件里有多个工作表要导入,可以加一层 for 循环遍历workbook.getSheets()。数据量大的时候,workbook.close()一定要放在 finally 里,否则文件句柄得不到释放,Windows 上还会出现“文件被占用”导致无法删除原始 xls。

3.2 比对更新:先 SELECT 再决定 INSERT 还是 UPDATE

拿到行列表后,最直接的做法是逐行执行 SELECT COUNT(*),判断记录是否存在。原工程的更新策略就是这种先查后插:

private static final String COUNT_SQL = "SELECT COUNT(*) FROM payment WHERE id = ?"; private static final String INSERT_SQL = "INSERT INTO payment(id, name, amount) VALUES (?, ?, ?)"; private static final String UPDATE_SQL = "UPDATE payment SET name = ?, amount = ? WHERE id = ?"; for (Map<String, String> row : rows) { countStmt.setString(1, row.get("id")); ResultSet rs = countStmt.executeQuery(); rs.next(); boolean exists = rs.getInt(1) > 0; if (!exists) { insertStmt.setString(1, row.get("id")); insertStmt.setString(2, row.get("name")); insertStmt.setString(3, row.get("amount")); insertStmt.executeUpdate(); } else { updateStmt.setString(1, row.get("name")); updateStmt.setString(2, row.get("amount")); updateStmt.setString(3, row.get("id")); updateStmt.executeUpdate(); } }

注意SELECT COUNT(*)即使没有记录也会返回一行,所以rs.next()之后直接getInt(1)是安全的,不需要判断 rs 是否为空。id 字段在 Excel 里带前后空格的话,先查的时候查不到,INSERT 第一次没问题,第二次同样 id 但带空格的记录又会插入,这就是重复数据的来源之一,所以读行时一定要 trim。所有 SQL 都用 PreparedStatement 占位符,避免字符串拼接带来的注入风险,也避免把数字写成字符串时的隐式转换混乱。

有人说可以省掉 SELECT,直接 INSERT,等主键冲突时捕获异常再 UPDATE。在小数据量下我更倾向先查后插,因为逻辑直观,后面改成批量也容易。如果表很大,COUNT 慢,可以给 id 加索引,或者改成INSERT ... ON DUPLICATE KEY UPDATE,让数据库自己判断。这个项目用先查后插,胜在兼容 MySQL 5.1 和 5.1.7 驱动,不依赖 MySQL 特有的 SQL 方言。

3.3 事务和批处理:别让一千行变成一千次自动提交

默认情况下,PreparedStatement 每次 executeUpdate 都会走一次自动提交。一千行就是一千次网络往返,慢到怀疑人生。原工程处理大量数据时,通常会在循环外关掉自动提交,累积到一定条数再提交:

Connection conn = DBHelper.getConnection(); conn.setAutoCommit(false); int count = 0; for (Map<String, String> row : rows) { // 填充 SQL 参数并执行 update if (++count % 500 == 0) { conn.commit(); } } conn.commit();

把 setAutoCommit(false) 放在循环前,每 500 行手动 commit 一次。这样即使中间某行出错,数据只会回滚到最近一次提交点,不会把整张表搞乱。如果你要保证整批数据要么全进要么全部不进,可以把 500 去掉,只在最后 commit 一次。两者取舍是:全量提交出问题后回滚成本低,但长事务会一直占着连接;分段提交适合超长文件,出错了重跑也只需要从最近的提交点继续。

更进一步的优化是用addBatch/executeBatch把多次 UPDATE 合并成一次批量执行,MySQL 连接串里再加上rewriteBatchedStatements=true,驱动会把连续的同一条 INSERT 拼接成多值语句,导入速度可以快一个数量级。这个连接串的写法在跑通章节再说。需要提醒的是,事务隔离级别如果没改,MySQL 默认的 REPEATABLE READ 会导致同一个连接里先查到的结果在事务中保持一致,也就是说,你在事务内 SELECT COUNT 判断不存在后 INSERT,只要没提交,别人也看不到这条新数据,这本身不会出问题,但会让调试时感觉数据“看不见”,查完记得 commit 再查。

3.4 反向导出:把 MySQL 结果集写回 .xls

导入的反向操作在导出类里。先 SELECT,再把 ResultSet 一行行写进 jxl 的 WritableWorkbook:

WritableWorkbook out = Workbook.createWorkbook(new File("result.xls")); WritableSheet sheet = out.createSheet("导出结果", 0); sheet.addCell(new Label(0, 0, "id")); sheet.addCell(new Label(1, 0, "name")); sheet.addCell(new Label(2, 0, "amount")); int row = 1; while (rs.next()) { sheet.addCell(new Label(0, row, rs.getString("id"))); sheet.addCell(new Label(1, row, rs.getString("name"))); sheet.addCell(new Label(2, row, rs.getString("amount"))); row++; } out.write(); out.close();

Label 构造器参数是 (列, 行, 内容),和读取时保持同一个坐标系。写完后必须调用write()和close(),只 close 不 write,生成的文件是 0 字节。导出一般没有更新逻辑,但对账很有用:导入完成后导出一份当前库里的数据,再和原 Excel 做行数比对,判断是否有记录被漏掉或更新错。这正是我在生产环境里用来验证导入结果的方法。

4. 跑通工程:从 Eclipse 导入到控制台看到第一行数据

4.1 用建表脚本把库和表准备好

先把压缩包解压,打开命令行进入项目根目录,执行下面的命令:

mysql -uroot -p < javenforexcel.sql

这条命令会读取 SQL 脚本,创建数据库和 payment 表。如果脚本里没有 CREATE DATABASE,只有 CREATE TABLE,那你就得先手工建库:

CREATE DATABASE excel_import DEFAULT CHARACTER SET utf8mb4;

实际运行前,我会先打开脚本看一遍,重点确认三件事:有没有建库语句、表名是什么、主键是哪个字段。有的脚本在别人机器上能跑,本地卡在 Unknown database,就是因为漏了 USE 语句。如果不想用命令行,也可以在 Navicat 或 DataGrip 里直接执行文件,效果一样。执行完可以用SHOW TABLES;确认表已经创建,再用SHOW CREATE TABLE payment;确认字符集和主键,这两步花不了十秒钟,能避免后面一大半的乱码和重复数据问题。

4.2 修改 DBHelper 里的连接参数

原工程的数据库连接信息大概率写在 DBHelper 的静态变量里,把它改成你自己的环境:

package com.excel; import java.sql.Connection; import java.sql.DriverManager; public class DBHelper { private static final String DRIVER = "com.mysql.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/excel_import" + "?useUnicode=true&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(URL, USER, PASSWORD); } }

参数含义:localhost:3306 是本机 MySQL 默认地址;excel_import 是 4.1 里建好的库名;useUnicode=true和characterEncoding=utf8是处理中文的老组合,MySQL 5.1.7 驱动靠这两个参数避免中文变问号。如果你本机 MySQL 是 8.x,要把 DRIVER 改成com.mysql.cj.jdbc.Driver,URL 里再加serverTimezone=Asia/Shanghai,否则连接会直接抛异常。密码里有@、#等特殊字符时,先确认 DriverManager 接收的是原始字符串,不要在配置文件里转义两次。

改完参数后,建议先单独验证连接。最简单的方式是在 main 方法里临时写一行System.out.println(DBHelper.getConnection());,能打印出 Connection 对象说明参数没问题,再继续往下走。最常见的问题其实不是业务代码,而是密码写错一个字符,或者 URL 里多了一个空格。

4.3 回到 Eclipse 运行 main 方法

回到 Eclipse,右键项目 Refresh,然后右键 src/com/excel/Main.java → Run As → Java Application。Main 里的流程一般是这样:

public class Main { public static void main(String[] args) throws Exception { // 1. 导入 Excel ExcelImporter.importer(new File("data.xls")); // 2. 验证:把当前表数据导出 ExcelExporter.exporter(new File("verify.xls")); System.out.println("done"); } }

注意工作目录。Eclipse 默认的工作目录是项目根目录,所以 data.xls 要放在项目根目录下,而不是 src 目录下。如果读不到文件,先看控制台的 FileNotFoundException 指向的是哪个目录,把文件放对位置。如果控制台没有任何输出,先看是不是 Run Configuration 里选错了 main 类。

跑完之后验证一下:登录 MySQL 执行SELECT * FROM payment;,看行数和 Excel 里的数据是否一致;再跑一次 main,第二次同样的数据应该走 UPDATE,表行数不变。这个“跑两遍”的验证方式,能同时覆盖 INSERT 和 UPDATE 两条路径,是判断导入逻辑是否正确最直接的方法。verify.xls 导出后打开看中文、金额、行数是否与原文件一致,如果一致,整个跑通流程就结束了。

4.4 不用 Eclipse:用 javac 和 java 手动跑

有时候开发机上没有 Eclipse,或者你只装了 JDK,也可以用命令行把这个项目跑起来。编译和运行命令如下:

javac -encoding UTF-8 -cp "lib/jxl.jar;lib/mysql-connector-java-5.1.7-bin.jar" -d bin src/com/excel/*.java java -cp "bin;lib/jxl.jar;lib/mysql-connector-java-5.1.7-bin.jar" com.excel.Main

Windows 上用分号;分隔 classpath,Linux 和 macOS 上用冒号:。-encoding UTF-8是给 javac 指定源码编码,如果源码本身是 GBK,这里的值要相应改成 GBK,否则中文注释和字符串字面量会变成乱码。-d bin让编译输出写到 bin 目录。这样跑起来后,日志和 Eclipse 里完全一样。手动跑能帮你理解 classpath 到底是怎么工作的,而不是依赖 IDE 自动帮你拼好。

5. 避坑指南:五个最容易翻车的地方和对应解法

5.1 中文全部变成 ? 或乱码

现象:导入后 SELECT 出来中文全是 ???,或者导出 Excel 后用 WPS 打开一片乱码。

原因:连接串没有指定 characterEncoding,MySQL 表字符集与 Java 字符串编码不一致;老 Eclipse 工程 .settings 里默认编码是 GBK,编译出来的 class 里的字符串是 GBK 字节,而表是 utf8mb4。

解决:先在 DBHelper 的 URL 里固定useUnicode=true&characterEncoding=utf8;再检查 .settings/org.eclipse.jdt.core.prefs 里的编码是不是 UTF-8,不是就手工改;最后确认表结构是 utf8mb4,而不是 latin1。查表字符集用SHOW CREATE TABLE payment;。这三个地方对齐,中文问题基本消失。如果源 xls 文件本身是 GBK 编码的旧文件,jxl 读取时可能读到乱码,可以改用Workbook.getWorkbook(new FileInputStream(file))的方式读取,必要时对读到的字符串做一次 ISO-8859-1 到 UTF-8 的转换。

5.2 ClassNotFoundException: com.mysql.jdbc.Driver

现象:Run As 之后直接抛 ClassNotFoundException,代码一行都没执行。

原因:lib 目录下的 jar 没有被加到 Eclipse 的 Build Path。Eclipse 只把 lib 当作普通文件夹,不会自动把里面的 jar 变成编译依赖。

解决:在项目上右键 → Properties → Java Build Path → Libraries → Add JARs,选中 lib 下的 mysql-connector-java-5.1.7-bin.jar 和 jxl.jar。点完 Apply 再 Run,通常就通了。如果已经加了还是报错,看看 bin 目录下有没有 class 文件,有时是编译输出目录没更新,Project → Clean 一下再重新编译。命令行模式跑的时候,确认-cp里的路径是 relative path 指向项目根目录,别在子目录里执行命令。

5.3 导入很慢,几千行要跑几十秒

现象:数据量只有两三千行,导入却要一分多钟,日志里每个 SQL 都打印一行。

原因:没有关自动提交,也没有用批量执行。PreparedStatement 默认每条 executeUpdate 独立事务,网络往返成本高。

解决:核心循环前conn.setAutoCommit(false),每 500 行 commit 一次;如果驱动是 5.1.7,考虑在 URL 上加rewriteBatchedStatements=true,并且改用addBatch/executeBatch。注意 5.1.7 对 rewriteBatchedStatements 的识别可能不如 8.x 理想,更实际的组合是先把驱动升级到 8.0.x,再配合批量接口。还可以在连接池层面把cachePrepStmts=true打开,让驱动缓存 PreparedStatement,避免每行都重新解析 SQL。

5.4 第二遍导入,行数翻倍

现象:同一个 Excel 文件跑两遍,第一遍 1000 行,第二遍变成 2000 行,该更新的没更新。

原因:判断是否存在的字段选错了。常见是 Excel 里的 id 列带了空格或前后不可见字符,先查时 COUNT 查不到;或者主键字段在数据库是 INT,在 Excel 是 00123 这样的字符串,隐式转换后匹配不上。

解决:读取 Excel 时统一 trim;Excel 的 id 如果是数字格式,在 Excel 里先设置文本格式,避免科学计数法;数据库表主键直接用 VARCHAR(32)。另外在表上建唯一索引,即使业务代码判断漏了,数据库也能挡住重复插入。建唯一索引的 SQL 是ALTER TABLE payment ADD UNIQUE KEY uk_id (id);,如果已经有重复数据,先清理再建索引。

5.5 金额和日期读出来是科学计数法

现象:Excel 里明明写的是 123456789.12,导入数据库变成 1.2345678912E8。

原因:jxl 的getContents()对数字单元格返回展示文本,当列宽不够或格式为科学计数法时,读到的就是 1E8 这类字符串。

解决:判断 Cell 类型,对数值单元格用((NumberCell) cell).getValue()拿到 double,再用BigDecimal.valueOf(v).toPlainString()转成普通字符串:

Cell cell = sheet.getCell(2, i); String amount; if (cell.getType() == CellType.NUMBER && cell instanceof NumberCell) { double v = ((NumberCell) cell).getValue(); amount = BigDecimal.valueOf(v).toPlainString(); } else { amount = cell.getContents().trim(); }

getType() == CellType.NUMBER判断是不是数字单元格,toPlainString()负责把 1.23E8 还原成 123000000,而不是 123000000.0。日期单元格同理,jxl 读出的是日期序列数,需要用 DateCell 的getDate()拿到 Java 的 Date 对象,再用 SimpleDateFormat 格式化,不能直接拿 getContents 拼 SQL。

6. 二次开发:把写死的导入逻辑改成配置驱动的通用导入器

6.1 用表头映射替代硬编码列序号

现在代码把 id、name、amount 写死在循环里,换一张表就要改三个地方。我的改法是先读 Excel 第一行表头,建立一个表头文本到数据库字段名的映射:

Map<String, String> headerMapping = new HashMap<>(); headerMapping.put("用户ID", "id"); headerMapping.put("姓名", "name"); headerMapping.put("金额", "amount"); Map<Integer, String> columnToField = new HashMap<>(); for (int c = 0; c < sheet.getColumns(); c++) { String header = sheet.getCell(c, 0).getContents().trim(); if (headerMapping.containsKey(header)) { columnToField.put(c, headerMapping.get(header)); } }

接下来读数据行时,根据 columnToField 动态拼 Map,而不是写死 getCell(0, i)。这样业务方新增一列时,只需要在 headerMapping 里加一项;Excel 列顺序变了我也不用改代码。这是一次很小的重构,收益却很大。常见的扩展点还有:把 headerMapping 放到一个 properties 文件里,不同客户用不同映射;或者直接在 Excel 里加一个“是否新增”的标志列,导入时根据标志决定用 INSERT 还是 UPDATE,而不是只能靠主键判断。

6.2 验证二次开发结果的方法

改造完先不要直接上正式数据。准备一个十行的测试 Excel,第一列故意放两个相同 id:一条走 INSERT,一条走 UPDATE。跑完 SELECT 确认行数比 10 少 1,且第二次运行表行数不变。金额列用科学计数法单元格、中文列用带前后空格的值,把 5.1 到 5.5 的坑全塞进测试数据里,一遍跑完就知道改没改坏。再加一个字段,改 headerMapping 后立刻重新导出 Excel 对账,确保新旧列都写进了文件。

从那以后我每次写这种文件导入任务,都会强制走一遍:先看源文件有哪些列、哪些是主键,再决定用先查后插还是 ON DUPLICATE KEY UPDATE,最后用跑两遍和行数比对收尾。这套动作比任何框架都管用。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询