19 de agosto de 2026 • Java

13 - Propagação de Transação Spring

Introdução

O gerenciamento de transações é uma das funcionalidades mais importantes fornecidas pelo Spring Framework. Embora a anotação @Transactional seja comumente usada para definir limites transacionais, compreender a Propagação de Transações é essencial quando múltiplos métodos transacionais interagem entre si.

A propagação de transações define como um método transacional deve se comportar quando é chamado por outro método transacional. Ela determina se um método deve:

  • Participar de uma transação existente
  • Criar uma nova transação
  • Executar sem uma transação
  • Lançar uma exceção se uma transação existe ou não existe

Sem as configurações de propagação adequadas, as aplicações podem experimentar commits inesperados, rollbacks, inconsistências de dados ou problemas de desempenho.


O que é Propagação de Transação?

A Propagação de Transação especifica como o Spring deve gerenciar transações quando um método transacional chama outro método transacional.

Considere o seguinte exemplo:

@Service
public class OrderService {

    @Transactional
    public void placeOrder() {
        paymentService.processPayment();
    }
}
@Service
public class PaymentService {

    @Transactional
    public void processPayment() {
        // Payment logic
    }
}

Quando placeOrder() chama processPayment(), o Spring deve decidir:

  • processPayment() deve usar a transação existente?
  • Deve criar uma nova transação?
  • Deve executar sem uma transação?

A resposta depende do modo de propagação configurado.


Por que a Propagação de Transação é Importante

A propagação de transações ajuda a definir limites de negócios e a controlar o comportamento transacional entre serviços.

Casos de uso comuns incluem:

  • Registro de auditoria
  • Processamento de pagamentos
  • Atualizações de estoque
  • Sistemas de notificação
  • Processamento em lote
  • Operações de relatórios

A propagação adequada garante:

  • Consistência dos dados
  • Comportamento de rollback previsível
  • Melhor separação das preocupações de negócio
  • Confiabilidade aprimorada do sistema

Como Configurar a Propagação

A propagação é configurada usando o atributo propagation de @Transactional.

@Transactional(propagation = Propagation.REQUIRED)
public void processOrder() {
    // business logic
}

O Spring oferece vários tipos de propagação.


Propagation.REQUIRED (Padrão)

O Que Faz

  • Participa da transação existente se houver uma.
  • Cria uma nova transação se não houver nenhuma.

Este é o modo de propagação padrão.

@Transactional
public void processOrder() {
    paymentService.processPayment();
}
@Transactional
public void processPayment() {
    // Payment logic
}

Cenário

processOrder()
    └── processPayment()

Resultado:

  • Uma transação
  • Ambos os métodos compartilham a mesma transação

Comportamento de Rollback

Se qualquer um dos métodos lançar uma exceção em tempo de execução:

throw new RuntimeException("Payment failed");

A transação inteira é revertida (rollback).

Order Insert     -> Rolled Back
Payment Insert   -> Rolled Back

Vantagens

  • Simples
  • Caso de uso mais comum
  • Mantém a consistência dos dados

Desvantagens

  • Transações grandes podem se tornar caras
  • Falhas afetam todas as operações

Propagation.REQUIRES_NEW

O Que Faz

  • Suspende a transação atual.
  • Cria uma transação completamente nova.
@Transactional
public void processOrder() {
    auditService.saveAudit();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAudit() {
    // Save audit log
}

Cenário

Transação A
    └── Transação B

A Transação B é independente.

Exemplo

@Transactional
public void placeOrder() {

    orderRepository.save(order);

    auditService.saveAudit();

    throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAudit() {
    auditRepository.save(audit);
}

Resultado

Order Insert -> Rolled Back
Audit Insert -> Committed

Porque a transação de auditoria é independente.

Casos de Uso Comuns

  • Logs de auditoria
  • Rastreamento de erros
  • Registro de eventos
  • Notificações

Vantagens

  • Commit independente
  • Rollback independente

Desvantagens

  • Mais sobrecarga no banco de dados
  • Criação de transação adicional

Propagation.SUPPORTS

O Que Faz

  • Participa de uma transação existente se houver uma.
  • Executa sem uma transação se não houver nenhuma.
@Transactional(propagation = Propagation.SUPPORTS)
public User findUser(Long id) {
    return repository.findById(id);
}

Cenário

Chamado de método transacional:

Transação Existe
    └── Participa da Transação

Chamado diretamente:

Nenhuma Transação
    └── Executa Normalmentes

Casos de Uso Comuns

  • Operações de leitura
  • Métodos utilitários

Vantagens

  • Leve
  • Flexível

Desvantagens

  • A disponibilidade da transação varia

Propagation.NOT_SUPPORTED

O Que Faz

  • Suspende qualquer transação existente.
  • Executa sem uma transação.
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void generateReport() {
    // Reporting logic
}

Cenário

Transação A
    └── Suspende A
          Executa sem transação

Casos de Uso Comuns

  • Relatórios grandes
  • Operações de muita leitura
  • Chamadas a serviços externos

Vantagens

  • Melhor desempenho para tarefas de longa duração

Desvantagens

  • Sem suporte a rollback

Propagation.MANDATORY

O Que Faz

  • Requer uma transação existente.
  • Lança uma exceção se nenhuma transação existe.
@Transactional(propagation = Propagation.MANDATORY)
public void updateInventory() {
    // Logic
}

Cenário

Transação Existe -> OK
Nenhuma Transação -> Exceção

Exceção

IllegalTransactionStateException

Casos de Uso Comuns

  • Métodos que devem sempre participar de uma transação de negócio

Vantagens

  • Impõe disciplina transacional

Desvantagens

  • Menos flexível

Propagation.NEVER

O Que Faz

  • Deve executar sem uma transação.
  • Lança uma exceção se uma transação existe.
@Transactional(propagation = Propagation.NEVER)
public void externalApiCall() {
    // Call external system
}

Cenário

Transação Existe -> Exceção
Nenhuma Transação -> OK

Exceção

IllegalTransactionStateException

Casos de Uso Comuns

  • Integrações não transacionais
  • APIs externas

Vantagens

  • Previne transações de longa duração

Desvantagens

  • Caso de uso muito especializado

Propagation.NESTED

O Que Faz

Cria uma transação aninhada dentro de uma transação existente usando savepoints.

@Transactional
public void processOrder() {
    inventoryService.reserveStock();
}
@Transactional(propagation = Propagation.NESTED)
public void reserveStock() {
    // Logic
}

Cenário

Transação Principal
    └── Savepoint
           Transação Aninhada

Comportamento de Rollback

Transação aninhada falha:

throw new RuntimeException();

O Spring pode fazer rollback para o savepoint, mantendo a transação externa ativa.

Exemplo

Order Insert       -> Committed
Inventory Reserve  -> Rolled Back

Vantagens

  • Capacidade de rollback parcial

Desvantagens

  • Requer suporte do banco de dados
  • Menos comumente usado

Tabela de Comparação de Propagação

PropagaçãoTransação ExistenteNenhuma Transação Existente
REQUIREDParticipaCria Nova
REQUIRES_NEWSuspende Atual + Cria NovaCria Nova
SUPPORTSParticipaExecuta Sem Transação
NOT_SUPPORTEDSuspende AtualExecuta Sem Transação
MANDATORYParticipaExceção
NEVERExceçãoExecuta Sem Transação
NESTEDTransação AninhadaCria Nova Transação

Exemplo do Mundo Real

Imagine um sistema de e-commerce.

@Transactional
public void checkout() {

    orderService.createOrder();

    paymentService.processPayment();

    auditService.saveAudit();
}

Configuração Recomendada

@Transactional
public void createOrder() {
}
@Transactional
public void processPayment() {
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAudit() {
}

Comportamento

Se o pagamento falhar:

Order -> Rollback
Payment -> Rollback
Audit -> Commit

Os logs de auditoria permanecem disponíveis para solução de problemas.


Erros Comuns

Usar REQUIRES_NEW Em Todos os Lugares

Má prática:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveSomething() {
}

A criação de muitas transações independentes aumenta:

  • Carga do banco de dados
  • Complexidade de bloqueio
  • Consumo de recursos

Use-o apenas quando for realmente necessário.


Esperar Suporte NESTED Em Todos os Lugares

Nem todos os bancos de dados e gerentes de transação suportam transações aninhadas.

Sempre verifique a compatibilidade do banco de dados.


Transações de Longa Duração

Evite:

@Transactional
public void processOrder() {

    repository.save(order);

    externalApi.call();

    Thread.sleep(30000);
}

Problemas:

  • Bloqueios permanecem ativos
  • Conexão permanece ocupada
  • Desempenho degrada

Melhores Práticas

Use REQUIRED como Padrão

Para a maioria das operações de negócio:

@Transactional
public void createOrder() {
}

O Spring usa REQUIRED automaticamente.


Use REQUIRES_NEW para Operações Independentes

Exemplos:

  • Logs de auditoria
  • Logs de erro
  • Rastreamento de eventos
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAudit() {
}

Mantenha as Transações Curtas

Bom:

@Transactional
public void createOrder() {
    repository.save(order);
}

Evite chamadas externas dentro de transações sempre que possível.


Separe Operações de Leitura e Escrita

Operações somente leitura:

@Transactional(readOnly = true)
public User findUser(Long id) {
    return repository.findById(id);
}

Isso pode melhorar o desempenho e reduzir bloqueios desnecessários.


Entenda os Limites de Rollback

Sempre saiba:

  • Qual transação é responsável pelo commit
  • Qual transação é responsável pelo rollback
  • Se os métodos filhos compartilham ou criam novas transações

Isso evita comportamentos inesperados em sistemas de produção.


Resumo

A Propagação de Transação determina como os métodos transacionais interagem quando um método chama outro. É um conceito crítico para projetar aplicações corporativas confiáveis e de fácil manutenção.

Os modos de propagação mais comumente usados são:

  • REQUIRED (padrão)
  • REQUIRES_NEW
  • SUPPORTS

Para a maioria da lógica de negócios, REQUIRED é suficiente. Use REQUIRES_NEW quando uma operação deve ser bem-sucedida independentemente da transação principal, como o registro de auditoria. Modos mais especializados como MANDATORY, NEVER, NOT_SUPPORTED e NESTED devem ser usados apenas quando suas semânticas transacionais específicas são necessárias.

Uma compreensão sólida da propagação de transações ajuda os desenvolvedores a construir sistemas mais previsíveis, resilientes e fáceis de manter.

← Voltar para o blog