mysql的on duplicate key update支持单条语句批量upsert:多行insert中每行独立触发冲突检测,update子句用values(col)引用对应行值,所有行共用同一更新逻辑,需主键或唯一索引生效。

MySQL 的 ON DUPLICATE KEY UPDATE 本身不支持直接对多行批量 INSERT 做“每行独立判断+更新”,但通过 JDBC 批量执行 + 合理 SQL 写法,可以高效实现类似效果。关键在于:用一条 INSERT ... ON DUPLICATE KEY UPDATE 语句插入多行,并在 UPDATE 子句中用 VALUES(col) 引用当前插入行的值。
SQL 层:单条语句完成批量 upsert
核心写法是把多条记录拼成一个 INSERT,配合 VALUES(col_name) 动态取值。例如:
INSERT INTO user (id, name, score, version) VALUES (1, 'Alice', 85, 1), (2, 'Bob', 92, 1), (3, 'Charlie', 78, 1) ON DUPLICATE KEY UPDATE name = VALUES(name), score = VALUES(score), version = VALUES(version) + 1;
注意:
- VALUES(name) 指“本次 INSERT 中对应位置那行的 name 值”,不是表中已有值;
- 主键或唯一索引冲突时才触发 UPDATE;
- 所有行共用同一套 UPDATE 表达式,不能为每行写不同逻辑。
JDBC 批量执行(推荐 PreparedStatement)
避免字符串拼接 SQL,用参数化方式安全高效地绑定多组值:
- 构造含多个
?占位符的 SQL(如(?, ?, ?, ?), (?, ?, ?, ?), ...); - 循环调用
ps.setXXX()设置每组参数,再调用ps.addBatch(); - 最后
ps.executeBatch()一次性提交。
示例片段(简化):
String sql = "INSERT INTO user (id, name, score, version) VALUES (?, ?, ?, ?) " +
"ON DUPLICATE KEY UPDATE name = VALUES(name), score = VALUES(score), version = VALUES(version) + 1";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
for (User u : users) {
ps.setLong(1, u.getId());
ps.setString(2, u.getName());
ps.setInt(3, u.getScore());
ps.setInt(4, u.getVersion());
ps.addBatch();
}
ps.executeBatch(); // 一次网络往返完成全部 upsert
}
注意事项与常见问题
唯一键必须存在且生效:确保表上有主键或 UNIQUE 索引,否则 ON DUPLICATE KEY UPDATE 不会触发。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
批量大小适中:单次插入不宜超过 1000 行(受 max_allowed_packet 和性能影响),可分批处理。
返回影响行数:JDBC 的 executeBatch() 返回的是每条语句的影响行数数组——插入新行算 1 行,更新已存在行也算 1 行。可通过它区分哪些是 insert、哪些是 update。
不支持条件更新:比如“仅当新 score > 旧 score 时才更新”,MySQL 8.0+ 可用 IF(VALUES(score) > score, VALUES(score), score) 模拟,但逻辑复杂时建议改用 MERGE 或业务层控制。
替代方案对比(何时不用 ON DUPLICATE KEY)
如果需要更复杂的冲突处理逻辑(如部分字段只 insert 不 update、多表关联判断、失败后自定义重试),可考虑:
- REPLACE INTO:先删后插,会丢失自增 ID 和触发 DELETE 触发器,慎用;
- INSERT IGNORE:冲突就跳过,无法更新;
- 先 SELECT 再 INSERT/UPDATE:并发下可能产生竞态,需加锁或事务隔离;
- MyBatis-Plus 的 saveOrUpdateBatch:底层仍是分批 select + insert/update,适合小批量或逻辑复杂场景。
对高吞吐、冲突率高的批量 upsert,原生 INSERT ... ON DUPLICATE KEY UPDATE + JDBC Batch 仍是性能最优解。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










