mcpField
T
transactional-patterns

transactional-patterns

github.com/rrezartprebreza开发328138 次安装6 天前更新
代码仓库

关于 transactional-patterns

transactional-patterns 是一项指导如何在应用代码中使用 Spring 事务的技能,帮助编码 Agent 保留现有架构,并处理传播、回滚、并发和分布式工作流。

主要功能

  1. 01选择事务边界
  2. 02理解事务传播行为
  3. 03处理回滚与异常
  4. 04应用乐观锁与重试
  5. 05用 Saga 协调分布式步骤
  6. 06管理提交后副作用与验证

适用场景

  1. 01修改服务前检查事务注解
  2. 02为受检异常设置回滚规则
  3. 03在新事务中处理乐观锁冲突
  4. 04规划提交后事件的持久化投递

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill transactional-patterns

安装后在 Claude Code 中输入/transactional-patterns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
examples/
bad-transactional-service.java
good-transactional-service.java
templates/
TransactionalOrderService.java

详细介绍

事务模式

明确边界

  • 更改传播行为前,检查项目的 Boot/Framework 版本、事务管理器、持久化技术、 回滚配置和调用方。
  • 在应用/服务边界处理多步骤业务事务。保留 Spring Data 仓库事务;仓库注解本身 并非错误。
  • 默认传播行为是 REQUIRED——加入现有事务,或创建新事务
  • 对适用的只读工作单元使用 readOnly = true。这是一种优化提示, 并不禁止写入、提供授权,也不会自动路由到只读副本。
  • 保留现有架构和事务管理器选择。本地数据库事务无法使写入另一个数据库或 HTTP 提供方的操作具备原子性。

以下代码片段仅作示例;领域类和导入项来自应用程序。 编辑服务时,请调整服务模板,并对照 良好示例和不良示例。

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true) // default for all methods in this service
public class OrderService {

    @Transactional // overrides readOnly for writes
    public Order createOrder(CreateOrderRequest request) {
        inventoryService.reserve(request.items()); // participates in same TX
        return orderRepository.save(Order.from(request));
    }

    public Optional<Order> findById(UUID id) {
        return orderRepository.findById(id); // readOnly = true inherited
    }
}

传播行为

传播行为行为
REQUIRED(默认)加入现有事务,或创建新事务
REQUIRES_NEW始终创建新事务,并挂起现有事务
SUPPORTS存在事务时加入;否则无事务地继续
NOT_SUPPORTED始终在无事务状态下运行
MANDATORY必须存在现有事务,否则抛出异常
NEVER必须不存在事务;若存在则抛出异常

参与事务的 REQUIRED 调用使用外层事务的隔离级别、超时和只读设置。捕获内部失败 不会清除其仅回滚标记;外层提交仍可能抛出 UnexpectedRollbackException。

外层事务保留资源时,REQUIRES_NEW 需要另一个连接。请考虑连接池容量和锁竞争。 仅将独立提交用于必须在操作失败后仍保留的记录(例如,尝试执行某操作的记录), 而不要用于声称已创建某个后来回滚订单的成功审计记录。不要在独立审计事务中引用 尚未提交的父记录。

// REQUIRES_NEW — for audit logging that must survive rollback
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logAuditEvent(AuditEvent event) {
    auditRepository.save(event); // commits independently of parent TX
}

// Illustrative: record an attempt for an existing order; failures are unchecked.
@Transactional
public void processOrder(Order order) {
    auditService.logAuditEvent(new AuditEvent("ORDER_START", order.getId()));
    try {
        // ... process, may throw
    } catch (RuntimeException e) {
        auditService.logAuditEvent(new AuditEvent("ORDER_FAILED", order.getId()));
        throw e; // parent TX rolls back, audit TX already committed
    }
}

自调用陷阱

// ❌ BROKEN — self-invocation bypasses Spring proxy, @Transactional ignored
@Service
public class OrderService {
    @Transactional
    public void processAll(List<UUID> ids) {
        ids.forEach(id -> this.processSingle(id)); // bypasses proxy!
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processSingle(UUID id) { ... } // never creates new TX
}

// ✅ FIX — extract the independently transactional operation to another bean
@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderProcessor orderProcessor; // separate bean

    @Transactional
    public void processAll(List<UUID> ids) {
        ids.forEach(id -> orderProcessor.processSingle(id)); // goes through proxy
    }
}

异常处理

// Default: RuntimeException and Error roll back; checked exceptions do not.
// Check project-wide rollback configuration before adding per-method rules.

@Transactional(rollbackFor = InsufficientInventoryException.class) // checked exception
public Order createOrder(CreateOrderRequest request) throws InsufficientInventoryException {
    ...
}

绝不要使用 noRollbackFor 来从 OptimisticLockException 中恢复:持久化提供方会将 事务标记为仅回滚状态。应回滚事务,并在全新的事务中重试整个工作单元,同时采用有 界重试策略并重新读取实体。

乐观锁

@Entity
public class Order {
    @Version
    private Long version; // Hibernate handles conflicts automatically
}

// Handles concurrent updates
@Transactional
public Order updateStatus(UUID id, OrderStatus newStatus) {
    Order order = orderRepository.findById(id).orElseThrow();
    order.updateStatus(newStatus); // conflict can surface at flush or commit
    return orderRepository.save(order);
}

有关依赖和注解导入,请使用针对版本的重试指南。 执行重试的外观层不应预先持有事务,否则每次尝试中的 REQUIRED 方法都会加入该事务。

分布式事务(Saga 模式)

Saga 用于协调持久化的本地步骤;用 @Transactional 包裹远程调用并不能实现 Saga。

  1. 在一个本地事务中提交待处理订单、Saga 状态和待发送命令。
  2. 在该事务之外发送命令,并使用稳定的提供方幂等键。
  3. 在另一个本地事务中,原子地持久化每个已确认的结果和下一条命令。
  4. 遇到拒绝时,持久化失败状态和补偿命令。独立重试补偿;对于远程结果未知的超时, 进行协调核对。

不要在同一个事务中先保存失败状态,然后再抛出会触发回滚的异常:这会抹掉该状态。 发生崩溃后,恢复流程必须从持久化状态继续。使用项目现有的工作流机制;有关消息 投递和重复效果,请参阅 消息传递/Outbox和 幂等性。

提交后的副作用

对于尽力而为的本地通知,绑定到提交的监听器可防止回滚时发送通知。 对于必须送达的通知,请将发件箱条目与业务变更一同持久化,或使用项目的 持久化发布机制。仅靠 AFTER_COMMIT 无法应对进程崩溃,也无法重试 发送失败的通知。

// Publisher — inside the TX
@Transactional
public Order place(UUID id) {
    Order order = orderRepository.findById(id).orElseThrow();
    order.place();
    eventPublisher.publishEvent(new OrderPlaced(order.getId())); // published now; listener defers handling
    return orderRepository.save(order);
}

// Listener — runs ONLY if the TX commits successfully
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderPlaced(OrderPlaced event) {
    emailService.sendConfirmation(event.orderId()); // best effort; no durable retry
}

AFTER_COMMIT 会在数据库提交后运行,但资源可能仍绑定到已完成的 事务。请将数据库写入委托给单独的 REQUIRES_NEW Bean。监听器失败 无法撤销已提交的业务写入;请确保发送失败可被发现并恢复。 默认情况下,没有活动事务时会跳过监听器。@Async 不会带来 持久化送达能力。请参阅领域事件。

验证

通过 Spring 代理进行测试,并在新事务中验证已提交的数据。覆盖受检异常 与非受检异常、内部回滚标记传播、提交时的乐观锁冲突、审计回滚,以及 提交后和回滚后的监听器行为。隔离性和竞争测试应使用生产环境的数据库 引擎。整个测试范围内的回滚可能会掩盖提交后行为。

常见问题

  • Agent 移除了仓库的事务注解——保留工作单元和现有 Spring Data 行为。
  • Agent 将 AFTER_COMMIT 视为可靠消息传递——对于必须送达的通知,请使用发件箱或持久化发布登记表。
  • Agent 使用 readOnly 进行副本路由或写入保护——仅凭此标志无法实现这两点。
  • Agent 使用 REQUIRES_NEW 提交成功审计条目——应与业务变更一同提交。
  • Agent 抑制乐观锁回滚——应在新事务中重试。
  • Agent 在 this 上调用 @Transactional 方法——自调用会绕过代理。
  • Agent 认为受检异常会触发回滚——必须添加 rollbackFor。
  • Agent 在 private 方法上使用 @Transactional——Spring 代理无法拦截。

官方来源

评论

支持 Markdown