重复提交异常本质是违反数据唯一性约束,需在全局异常处理器中通过解析SQLState或ErrorCode精准识别,并按业务字段映射友好提示,统一返回标准化JSON响应,同时辅以前端防重、数据库索引和Redis幂等校验。

重复提交引发的异常,本质是业务操作违反了数据唯一性约束,比如插入已存在的订单号、用户名或手机号。这类异常不能靠前端拦截或简单重试解决,必须在全局异常处理器中精准识别、友好转化、安全响应。
识别数据库唯一约束冲突
不同数据库抛出的底层异常不同,但上层框架(如 Spring)通常统一为 DuplicateKeyException 或 DataIntegrityViolationException。关键不是看异常类名,而是解析其根因:
- 从异常的
getRootCause()获取原生 SQLException - 读取
getSQLState()(标准值如'23000')或getErrorCode()(MySQL 是 1062,SQL Server 是 2627/2601) - 避免依赖异常消息字符串做正则匹配——数据库方言和版本升级会导致格式变化
按业务字段映射具体提示
用户需要知道“哪里重复了”,而不是“数据库报错了”。这要求后端能定位到触发唯一约束的字段:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 建索引时统一命名规范,例如
uk_user_phone、uk_order_no,再从异常信息中提取索引名 - 结合业务逻辑预检(如先查一次
userMapper.selectByPhone(phone)),虽多一次查询,但提示精准、可控性强 - 对同一异常类型,根据调用上下文区分语义:注册时冲突提示“手机号已被注册”,修改邮箱时提示“该邮箱已被其他账户使用”
在全局处理器中统一响应
用 @RestControllerAdvice 拦截,配合自定义 Result 结构返回标准化 JSON:
- 状态码用业务码(如 400),不暴露 HTTP 500 或数据库错误码
- 消息内容明确、无技术术语,例如
"订单号 ORD20260924001 已存在" - 记录完整异常堆栈到日志系统,但不返回给前端
- 对非重复类的
DataIntegrityViolationException(如外键失败、字段超长)应单独处理,避免误判为重复提交
补充建议:别只靠异常捕获兜底
全局异常处理是最后一道防线,不是唯一手段:
- 数据库层必须建好对应业务场景的唯一索引(如
(user_id, product_id)而非仅id) - 前端提交后禁用按钮 + 接口加防重 Token(服务端生成并校验一次有效)
- 高并发写入场景可叠加 Redis 幂等校验,提前拦截,减少数据库压力

















