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ção | Transação Existente | Nenhuma Transação Existente |
|---|---|---|
| REQUIRED | Participa | Cria Nova |
| REQUIRES_NEW | Suspende Atual + Cria Nova | Cria Nova |
| SUPPORTS | Participa | Executa Sem Transação |
| NOT_SUPPORTED | Suspende Atual | Executa Sem Transação |
| MANDATORY | Participa | Exceção |
| NEVER | Exceção | Executa Sem Transação |
| NESTED | Transação Aninhada | Cria 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_NEWSUPPORTS
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.