应专门捕获SQLIntegrityConstraintViolationException,通过getMessage()含“Duplicate entry”或getSQLState()为23000/23505精准识别主键冲突,再按场景返回友好提示、转更新操作或跳过冲突行,并优先采用数据库自增主键等前置防御措施。

Java 中通过 JDBC 处理主键冲突异常,核心是精准识别 + 场景化响应 + 前置防御,而不是简单 try-catch 吞掉异常。
捕获并准确识别主键冲突
SQLIntegrityConstraintViolationException 是 SQLException 的子类,不能笼统 catch SQLException。应专门捕获它,并通过异常信息判断是否为主键冲突:
- 检查 e.getMessage() 是否包含 "Duplicate entry ... for key 'PRIMARY'"(MySQL)或 "violates unique constraint \"xxx_pkey\""(PostgreSQL)
- 也可用 e.getSQLState() 辅助判断:MySQL 主键冲突通常为 23000,PostgreSQL 为 23505
- 避免仅靠异常类型判断——同一异常也可能由唯一索引、外键等触发,必须结合提示文本确认是主键
按业务场景选择处理方式
不同场景下,应对策略差异很大:
- 用户主动提交(如注册、录入单号):捕获后转为友好提示,例如“该编号已被使用,请更换”,不暴露数据库细节
- 幂等写入(如同步任务、消息消费):改用 INSERT ... ON DUPLICATE KEY UPDATE(MySQL)或 INSERT ... ON CONFLICT DO UPDATE(PostgreSQL),避免异常抛出
- 后台批量导入:用 INSERT IGNORE(MySQL)或 ON CONFLICT DO NOTHING(PG)跳过冲突行,再单独记录失败项
从源头减少冲突发生
防御优于补救,重点在设计阶段规避:
立即学习“Java免费学习笔记(深入)”;
- 主键字段尽量交由数据库自增(AUTO_INCREMENT / SERIAL),Java 层不手动 set ID
- 若需应用层生成 ID,优先选用 UUID 或 Snowflake 算法,而非时间戳+随机数等易碰撞方案
- 对业务关键字段(如手机号、工号)建 UNIQUE INDEX,让数据库强制校验,而非仅靠代码 SELECT 再插入(存在竞态)
- 使用 PreparedStatement 预编译语句,配合事务控制,确保查重与插入的原子性(如 SELECT ... FOR UPDATE)
调试时快速定位问题根源
光看异常堆栈不够,要拿到真实执行上下文:
- 开启 JDBC 日志(如 MySQL 加 ?logger=Slf4JLogger&profileSQL=true),查看实际执行的 SQL 和绑定参数
- 检查建表语句:SHOW CREATE TABLE table_name,确认主键类型、是否自增、有无触发器干扰
- 验证插入前数据状态:用相同 ID 手动 SELECT,确认是否真已存在;注意区分大小写、空格、NULL 等隐式差异


















