mcpbeat Sign in

Transactional Patterns Agent Skill

> Use when working with @Transactional, multi-step database operations, distributed transactions, or any code that needs atomicity guarantees. Covers propagation rules, isolation levels, read-only optimization, and common pitfalls.

3k tokens
context cost
the whole folder, loaded on every use
4
files
instructions only
0
copies elsewhere
how many repositories repackaged it
190
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill transactional-patterns

The instruction itself

9 sections, as written by the author

Transactional Patterns

Basic Rules

  • @Transactional belongs on service methods, never controllers or repositories
  • Default propagation is REQUIRED — joins existing transaction or creates one
  • Always use on methods that write to the DB or coordinate multiple writes
  • @Transactional(readOnly = true) on all read-only service methods — enables optimizations
@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

| 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 |

// 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
}

// Order TX rolls back, audit log still saved
@Transactional
public void processOrder(Order order) {
    auditService.logAuditEvent(new AuditEvent("ORDER_START", order.getId()));
    try {
        // ... process, may throw
    } catch (Exception e) {
        auditService.logAuditEvent(new AuditEvent("ORDER_FAILED", order.getId()));
        throw e; // parent TX rolls back, audit TX already committed
    }
}

Self-Invocation Pitfall

// ❌ 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 — inject self or extract to separate 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
    }
}

Handling Exceptions

// @Transactional rolls back on RuntimeException by default
// For checked exceptions, explicitly declare rollbackFor

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

// noRollbackFor — for non-fatal exceptions you want to commit anyway
@Transactional(noRollbackFor = OptimisticLockException.class)
public void updateWithRetry(UUID id) { ... }

Optimistic Locking

@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); // if another TX modified it, throws ObjectOptimisticLockingFailureException
    return orderRepository.save(order);
}

Distributed Transactions (Saga Pattern)

For multi-service operations, use the Saga pattern instead of distributed TX:

@Service
@RequiredArgsConstructor
public class OrderSaga {

    @Transactional
    public void execute(CreateOrderRequest request) {
        Order order = orderRepository.save(Order.create(request));
        try {
            inventoryClient.reserve(request.items());       // step 1
            paymentClient.charge(order.getId(), request.total()); // step 2
            order.confirm();
            orderRepository.save(order);
        } catch (PaymentException e) {
            inventoryClient.release(request.items()); // compensate step 1
            order.fail("Payment failed");
            orderRepository.save(order);
            throw e;
        }
    }
}

Side Effects After Commit

Never fire an external side effect (email, Kafka publish, webhook, cache warm) inside the transaction —

if the TX rolls back, you've already sent it. Bind the side effect to the commit instead:

// Publisher — inside the TX
@Transactional
public Order place(UUID id) {
    Order order = orderRepository.findById(id).orElseThrow();
    order.place();
    eventPublisher.publishEvent(new OrderPlaced(order.getId())); // not sent yet
    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()); // safe: data is durable
}

AFTER_COMMIT runs after the DB commits. Note: it runs outside the original transaction, so a

new @Transactional(REQUIRES_NEW) is needed if the listener itself writes to the DB. This is the

clean way to publish the domain events collected in the [[domain-driven-design]] aggregate.

Gotchas

  • Agent puts @Transactional on controllers — only on service layer
  • Agent sends email / publishes events inside the TX — use @TransactionalEventListener(AFTER_COMMIT)
  • Agent forgets readOnly = true on read methods — missed DB optimization
  • Agent calls @Transactional methods on this — self-invocation bypasses proxy
  • Agent expects checked exceptions to rollback — must add rollbackFor
  • Agent uses @Transactional on private methods — Spring proxy can't intercept

Other skills for the same job

different authors, same section of the catalogue
Biorxiv Database
by christophacham
×4

Efficient database search tool for bioRxiv preprint server. Use this skill when searching for life sciences preprints by keywords, authors, date ranges, or categories, retrieving paper metadata, downloading PDFs, or conducting literature reviews.

9k tokens scripts
Brenda Database
by christophacham
×4

Access BRENDA enzyme database via SOAP API. Retrieve kinetic parameters (Km, kcat), reaction equations, organism data, and substrate-specific enzyme information for biochemical research and metabolic pathway analysis.

36k tokens scripts
Clinpgx Database
by christophacham
×4

Access ClinPGx pharmacogenomics data (successor to PharmGKB). Query gene-drug interactions, CPIC guidelines, allele functions, for precision medicine and genotype-guided dosing decisions.

13k tokens scripts
Clinvar Database
by christophacham
×4

Query NCBI ClinVar for variant clinical significance. Search by gene/position, interpret pathogenicity classifications, access via E-utilities API or FTP, annotate VCFs, for genomic medicine.

10k tokens
Cosmic Database
by christophacham
×4

Access COSMIC cancer mutation database. Query somatic mutations, Cancer Gene Census, mutational signatures, gene fusions, for cancer research and precision oncology. Requires authentication.

6k tokens scripts
Ensembl Database
by christophacham
×4

Query Ensembl genome database REST API for 250+ species. Gene lookups, sequence retrieval, variant analysis, comparative genomics, orthologs, VEP predictions, for genomic research.

8k tokens scripts
Fda Database
by christophacham
×4

Query openFDA API for drugs, devices, adverse events, recalls, regulatory submissions (510k, PMA), substance identification (UNII), for FDA regulatory data analysis and safety research.

32k tokens scripts
Gene Database
by christophacham
×4

Query NCBI Gene via E-utilities/Datasets API. Search by symbol/ID, retrieve gene info (RefSeqs, GO, locations, phenotypes), batch lookups, for gene annotation and functional analysis.

13k tokens scripts

How to use it

Copy the folder

Take rrezartprebreza/transactional-patterns from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.