ai-observability
Use when adding Spring AI-specific model observations, token usage, latency, externally configured cost attribution, advisor…
Use when defining or debugging Spring Boot 3 database transaction boundaries, rollback, propagation, optimistic-lock retries, or commit-bound events. For duplicate HTTP commands use idempotency-patterns; for reactive transactions use webflux-reactive-patterns.
Claude Code
$ npx -y skills add rrezartprebreza/spring-boot-skills --skill transactional-patterns --agent claude-code
Codex
$ npx skills add rrezartprebreza/spring-boot-skills --skill transactional-patterns -a codex -y
Cursor
$ npx skills add rrezartprebreza/spring-boot-skills --skill transactional-patterns -a cursor -y
Gemini CLI
$ npx skills add rrezartprebreza/spring-boot-skills --skill transactional-patterns -a gemini-cli -y
GitHub Copilot
$ npx skills add rrezartprebreza/spring-boot-skills --skill transactional-patterns -a github-copilot -y
OpenCode
$ npx skills add rrezartprebreza/spring-boot-skills --skill transactional-patterns -a opencode -y
Windsurf
$ npx skills add rrezartprebreza/spring-boot-skills --skill transactional-patterns -a windsurf -y
name: transactional-patterns description: > Use when defining or debugging Spring Boot 3 database transaction boundaries, rollback, propagation, optimistic-lock retries, or commit-bound events. For duplicate HTTP commands use idempotency-patterns; for reactive transactions use webflux-reactive-patterns.
rollback configuration and caller before changing propagation.
Spring Data repository transactions; repository annotations are not inherently wrong.
not write prevention, authorization or automatic routing to a read replica.
does not make writes to another database or an HTTP provider atomic.
The snippets below are illustrative; domain classes and imports come from the application. Adapt the [service template](templates/TransactionalOrderService.java) and compare the [good](examples/good-transactional-service.java) and [bad](examples/bad-transactional-service.java) examples when editing a service.
@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
}
}| Propagation | Behavior | |-------------|----------| | `REQUIRED` (default) | Join existing TX or create new | | `REQUIRES_NEW` | Always create new TX, suspend existing | | `SUPPORTS` | Join if exists, proceed without TX if not | | `NOT_SUPPORTED` | Always run without TX | | `MANDATORY` | Must have existing TX, throw if not | | `NEVER` | Must NOT have TX, throw if one exists |
Participating `REQUIRED` calls use the outer transaction's isolation, timeout and read-only settings. Catching an inner failure does not clear its rollback-only marker; outer commit may still throw `UnexpectedRollbackException`.
`REQUIRES_NEW` needs another connection while the outer transaction retains its resources. Account for pool capacity and lock contention. Reserve independent commits for records that must survive a failed operation (for example, an attempted action), not a success audit row that claims a rolled-back order was created. Do not reference an uncommitted parent row from an independent audit transaction.
// 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 {
...
}Never use `noRollbackFor` to recover from `OptimisticLockException`: the persistence provider marks the transaction rollback-only. Roll it back and retry the whole unit of work in a fresh transaction, with a bounded policy and a fresh entity read.
@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);
}Use the [version-specific retry guidance](../resilience-retry/SKILL.md) for dependencies and annotation imports. A retrying facade must not already hold a transaction that each attempt's `REQUIRED` method would join.
A saga coordinates durable local steps; wrapping remote calls in `@Transactional` does not implement one.
1. Commit the pending order, saga state and outgoing command in one local tra
Production-grade Claude Code and Codex skills for Spring Boot developers
Use when adding Spring AI-specific model observations, token usage, latency, externally configured cost attribution, advisor…
Use when versioning Spring MVC or WebFlux APIs in Spring Boot 3 / Spring Framework 6. Covers explicit URL, header, and media-type…
Use when introducing or correcting grouped Spring Boot configuration, typed property binding, validation, profiles or secret…
Use when packaging a Spring Boot 3 application as an OCI image or GraalVM native executable. Covers buildpacks, layered images,…
Use when evolving aggregate invariants, value objects or domain events in an existing DDD-style Spring Boot 3 application, or…
Use when implementing Kafka, RabbitMQ, Pulsar, or JMS producers and consumers in Spring Boot 3. Covers event contracts,…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.