Java事务中实现幂等性需事务与幂等机制协同:用唯一id+状态标记前置校验,数据库唯一约束兜底,状态机条件更新,幂等日志与业务操作同事务。

Java 事务中设计幂等性接口,核心不是靠事务本身“解决重复”,而是用事务配合幂等机制来保证:即使请求重复进来,最终数据库状态也只变更一次。事务管的是“执行过程的原子性与一致性”,幂等性管的是“多次调用的结果等价于一次”。两者必须协同,不能互相替代。
用唯一标识 + 状态标记做前置校验
在 Service 方法入口(而非 Controller)立即校验该请求是否已处理。关键点有三个:
- 客户端必须携带幂等键(idempotentId),推荐用
UUID.randomUUID().toString().replace("-", "")生成,服务端不信任前端传的时间戳或随机数 - 校验逻辑走 Redis 或数据库查记录,例如:
SELECT 1 FROM idempotent_log WHERE idempotent_id = ? AND status = 'success' - 命中即返回原始成功结果(含订单号、支付状态等),不进事务;未命中才开启新事务执行业务
数据库唯一约束作为强兜底
事务内执行核心写操作时,必须依赖数据库层面的唯一性保障,不能只靠应用层判断:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 创建类操作(如下单、注册):在业务表加唯一索引,字段选业务天然唯一值(如 order_no、user_phone)
- 插入失败时捕获
DataIntegrityViolationException,不抛出错误,而是查出已有记录并返回其完整数据 - 慎用
INSERT IGNORE—— 它静默失败,无法区分“真插入”和“被忽略”;优先用ON DUPLICATE KEY UPDATE并检查影响行数
状态机驱动更新类操作
对需要多次修改同一资源的场景(如支付回调、发货确认),不能简单“查-改-存”,要用条件更新确保幂等:
立即学习“Java免费学习笔记(深入)”;
- 订单表增加 status 和 version 字段,初始 status = 'created',version = 0
- 更新语句带状态前置条件:
UPDATE order SET status = 'paid', version = version + 1 WHERE id = ? AND status = 'created' AND version = ? - 用
updateCount == 1判断是否真正执行了状态跃迁,否则说明已被其他请求处理过
事务边界要包裹“校验+写入+标记”全过程
幂等记录(如 idempotent_log 表)的插入必须和业务操作在同一个数据库事务中完成,否则会出现“业务成功但幂等标记失败”的脏态:
- 先执行业务逻辑(如扣库存、生成订单)
- 再插入幂等日志:
INSERT INTO idempotent_log (idempotent_id, status, result_json) VALUES (?, 'success', ?) - 整个方法加
@Transactional,确保二者要么都成功,要么都回滚 - 避免“先插日志再执行业务”——中间失败会导致日志残留,后续请求被误拒

















