处理联合唯一索引冲突需结合数据库特有语法(如mysql的on duplicate key update、postgresql的on conflict)与java异常捕获,优先用原生upsert提升性能,兼容性不足时回退至sqlintegrityconstraintviolationexception捕获兜底。

当数据库表存在多列联合唯一索引(如 (user_id, category)),用 PreparedStatement 插入数据时若违反该约束,会抛出 SQLIntegrityConstraintViolationException(或其子类,如 MySQL 的 MySQLIntegrityConstraintViolationException)。Java 本身不提供“自动忽略冲突”的语法,但可通过异常捕获 + 业务逻辑兜底、或借助数据库特有语法(如 ON DUPLICATE KEY UPDATE、MERGE)来处理。关键在于:不能只靠 PreparedStatement 本身,而要结合 SQL 方式与 Java 异常处理协同解决。
捕获唯一索引冲突异常并降级处理
这是最通用、跨数据库的方式。执行 INSERT 后,若失败则转为更新、跳过或记录日志。
- 使用标准
INSERT INTO ... VALUES (?, ?),参数通过setLong(1, userId)、setString(2, category)绑定 - 用
try-catch捕获SQLIntegrityConstraintViolationException - 检查异常信息是否包含联合索引字段名(如
"user_id_category"或"Duplicate entry"),避免误判主键冲突或其他约束 - 根据业务决定后续动作:比如调用
updateXXX()方法,或直接return false表示插入失败但无需报错
用数据库特有语法实现“存在则忽略/更新”
比纯异常捕获更高效,减少网络往返和异常开销,但需适配具体数据库。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
MySQL:用
INSERT ... ON DUPLICATE KEY UPDATE,例如:INSERT INTO user_category (user_id, category, created_at) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE updated_at = NOW()
注意:必须确保user_id, category上建了唯一索引(联合唯一),否则不触发 -
PostgreSQL:用
INSERT ... ON CONFLICT DO NOTHING / UPDATE,例如:INSERT INTO user_category (user_id, category, created_at) VALUES (?, ?, ?) ON CONFLICT (user_id, category) DO NOTHING -
Oracle:用
MERGE INTO,需写完整WHEN MATCHED THEN UPDATE ... WHEN NOT MATCHED THEN INSERT ...,参数绑定方式相同,但 SQL 更复杂
预检查是否存在(慎用)
先 SELECT COUNT(*) 判断联合键是否已存在,再决定是否插入。看似直观,但有明显缺陷:
- 并发场景下存在竞态条件(check-then-act 问题):两次请求同时查到“不存在”,然后都执行插入,仍会触发唯一冲突
- 额外一次数据库查询,性能开销翻倍
- 仅适合低并发、强一致性要求不高的离线批处理场景
封装成可复用的工具方法
把冲突处理逻辑提取为工具类方法,避免每个 DAO 都重复写 try-catch 或拼 SQL:
- 定义一个
InsertOrUpdateResult枚举(如INSERTED、UPDATED、IGNORED) - 提供统一方法:
insertOrUpdateByUniqueKey(Connection conn, String insertSql, Object... params),内部根据数据库类型选择对应 SQL 模板 - 对 MySQL/PostgreSQL 等支持 upsert 的库,优先走原生语法;对 H2、Derby 等不支持的,回退到异常捕获模式
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










