Spring boot transaction propagation costuma dar problema quando a API precisa responder erro claro sem vazar regra de negocio. Se a sua transação quebra em produção sem explicação óbvia, o problema costuma estar em essa validacao, não no banco. Por outro lado, em Spring Boot, propagation define o que acontece quando um método já transacional chama outro, enquanto isolation controla o que cada transação consegue enxergar no banco. Depois de ajustar esse trecho, o proximo passo natural e seguir para Validar DTO Spring Boot com mensagens personalizadas sem erro.
Esse detalhe muda tudo em fluxo de pedido, pagamento, auditoria e integrações. Por outro lado, um service pode salvar metade do estado, outro pode abrir uma nova transação sem você perceber e, sob concorrência, leituras diferentes podem aparecer no mesmo caminho de negócio. Esse ponto fica mais claro quando voce conecta com JWT Spring Boot Java: Guia Completo com Exemplo Prático.
Spring boot transaction propagation: secao pratica com codigo completo
Na pratica, um exemplo enxuto ajuda a sair da teoria e evitar erro comum de producao quando o projeto cresce. Esse ponto fica mais claro quando voce conecta com JWT no Spring Security com Spring Boot: autenticação moderna passo a passo.
@RestController
@RequestMapping("/api/exemplo")
public class ExemploController {
@GetMapping
public ResponseEntity<String> listar() {
return ResponseEntity.ok("ok");
}
}Spring boot transaction propagation e spring transaction isolation na prática
Propagation e isolation parecem conceitos de documentação, mas o efeito aparece no dia a dia: um pedido confirmando estoque, um pagamento registrado antes do commit principal, uma atualização concorrente sobrescrevendo valor antigo. Por outro lado, entender essa validacao evita esse tipo de surpresa. Esse ponto fica mais claro quando voce conecta com API REST Spring Boot Java: Guia Completo com Exemplo Prático.
Propagation: o que fazer quando já existe transação
Propagation responde a uma pergunta simples: se um método transacional chamar outro, ele entra na transação atual ou abre outra? Por outro lado, o comportamento padrão, REQUIRED, participa da transação existente. Ao mesmo tempo, se não houver nenhuma, cria uma nova. Na prática, isso cobre boa parte dos serviços de CRUD e fluxo de negócio clássico. Depois de ajustar esse trecho, o proximo passo natural e seguir para Transactional readOnly no Spring Boot: quando ajuda de verdade.
Quando você usa REQUIRES_NEW, o método suspende a transação atual e cria outra independente. Por outro lado, isso é útil para auditoria, registro de falha ou persistência de log que não pode morrer junto com o fluxo principal. Ao mesmo tempo, só que esse “seguro extra” tem custo: mais idas ao banco, mais conexões e possibilidade de inconsistência entre o que foi salvo no processo principal e o que foi salvo fora dele.
SUPPORTS participa se já existir transação, mas não obriga nenhuma. Por outro lado, NOT_SUPPORTED faz o oposto: executa sem transação, suspendendo a atual. Ao mesmo tempo, MANDATORY, NEVER e NESTED aparecem menos no dia a dia, mas existem para cenários bem específicos. Na prática, em sistemas comuns, o trio que mais aparece é REQUIRED, REQUIRES_NEW e SUPPORTS.
Isolation: o que cada transação enxerga
Isolation é outra conversa. Por outro lado, aqui a pergunta não é “quem participa da transação”, e sim “qual nível de concorrência eu tolero”. Ao mesmo tempo, spring transaction isolation controla anomalias como dirty read, non-repeatable read e phantom read, dependendo do banco e do nível configurado.
Na prática, a maioria dos projetos funciona bem com o padrão do banco. Por outro lado, isso acontece porque subir demais o isolation custa caro. Ao mesmo tempo, menos concorrência significa mais bloqueio, mais espera e, em carga alta, timeouts que parecem aleatórios. Na prática, o ganho existe, mas precisa compensar o custo. Ainda assim, em sistemas de alto volume, mexer no isolation sem motivo costuma piorar a situação.
Erros comuns com spring boot transaction propagation
O erro mais frequente é imaginar que toda chamada entre métodos compartilha a mesma regra transacional só porque ambos têm @Transactional. Por outro lado, isso não é sempre verdade. Ao mesmo tempo, em Spring, a anotação depende de proxy. Na prática, se o método chama outro método no mesmo bean, a interceptação pode não acontecer como você espera.
Outro problema clássico é colocar transação no repositório e deixar o service “solto”. Por outro lado, o resultado parece funcionar no teste, mas quebra quando a regra passa por mais de uma operação. Ao mesmo tempo, o service deveria concentrar a unidade de trabalho. Na prática, o repositório deveria persistir dados, não decidir o limite de consistência do caso de uso.
Erro comum em produção: auditoria salva e negócio falha
Causa: alguém usa REQUIRES_NEW para registrar auditoria sem medir o impacto no fluxo principal.
Sintoma: o pedido falha, mas o log de auditoria ou a tabela de evento fica gravado como se o processo tivesse sido concluído.
Correção: use essa propagação apenas quando a independência for intencional. Por outro lado, se a auditoria precisa refletir exatamente o resultado do negócio, mantenha a mesma transação e trate o evento depois do commit, com outro desenho arquitetural.
Erro comum em produção: atualização concorrente some
Causa: duas requisições alteram o mesmo registro com isolation padrão e sem controle de concorrência otimista ou pessimista.
Sintoma: o último update vence, campos mudam sem aviso e o usuário vê dado antigo reescrito por outra operação.
Correção: além de avaliar spring transaction isolation, considere versionamento da entidade com @Version e ajuste o modelo de escrita para evitar sobrescrita silenciosa.
Na prática, a pergunta certa não é “qual isolation é mais forte?”. Por outro lado, a pergunta é “qual anomalia realmente precisa ser evitada aqui?”. Ao mesmo tempo, isso reduz custo e deixa a solução mais previsível.
Transactional spring boot exemplos com código completo
A melhor forma de enxergar transactional spring boot exemplos é olhar um caso de negócio simples: criação de pedido com reserva de estoque e registro de auditoria. Por outro lado, o fluxo principal deve ser atômico. Ao mesmo tempo, o log pode seguir uma regra diferente, dependendo do requisito.
Exemplo 1: fluxo principal com REQUIRED
package com.javalizando.orders.service;
import com.javalizando.orders.domain.Order;
import com.javalizando.orders.domain.OrderStatus;
import com.javalizando.orders.repository.OrderRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final StockService stockService;
public OrderService(OrderRepository orderRepository, StockService stockService) {
this.orderRepository = orderRepository;
this.stockService = stockService;
}
@Transactional
public Order createOrder(Order order) {
stockService.reserve(order.getProductId(), order.getQuantity());
order.setStatus(OrderStatus.CREATED);
return orderRepository.save(order);
}
}
package com.javalizando.orders.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StockService {
@Transactional
public void reserve(Long productId, Integer quantity) {
// valida estoque, registra movimentação e falha se não houver saldo
}
}
Com REQUIRED, se reserve falhar, o pedido inteiro volta. Por outro lado, esse comportamento é o que você espera quando a regra é “ou tudo acontece, ou nada acontece”.
Exemplo 2: auditoria com REQUIRES_NEW
package com.javalizando.orders.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AuditService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void registerFailure(String message) {
// grava auditoria independente do fluxo principal
}
}
Esse desenho faz sentido quando você quer registrar falhas técnicas ou eventos operacionais mesmo se a transação principal cair. Por outro lado, em produção, isso ajuda em rastreabilidade de incidentes. Ao mesmo tempo, se o log precisa representar apenas o que foi confirmado, a abordagem deixa de ser adequada.
Comparação direta: melhor e pior abordagem
Melhor abordagem: o service coordena a regra de negócio, o repositório só persiste, e a propagação é escolhida de acordo com a necessidade real do caso de uso. Por outro lado, o resultado é previsível, testável e mais fácil de manter.
Pior abordagem: espalhar @Transactional em métodos isolados, dentro de repositório, helper e fluxo de integração, esperando que o Spring “se vire”. Por outro lado, isso cria efeito colateral difícil de enxergar, principalmente quando a chamada é interna ou quando a aplicação cresce para múltiplos serviços.
Quando usar spring boot transaction propagation e quando evitar
Use propagation para refletir a unidade de trabalho do processo. Por outro lado, se o caso de uso exige consistência entre várias operações, mantenha uma transação única com REQUIRED. Ao mesmo tempo, se um passo precisa sobreviver ao erro do restante, aí sim REQUIRES_NEW vira uma ferramenta útil.
Evite subir isolation sem sintoma comprovado. Por outro lado, em sistemas web, o padrão do banco costuma ser suficiente para a maioria dos endpoints. Ao mesmo tempo, quando há conflito de escrita ou leitura inconsistente, muitas vezes o verdadeiro ajuste é no desenho da concorrência, no uso de @Version ou na divisão do fluxo, e não no isolation.
Dois cenários reais ajudam a decidir. Por outro lado, em um checkout de e-commerce, reservar estoque, criar pedido e registrar pagamento precisam caminhar juntos; separar isso por conveniência gera inconsistência e suporte caro depois. Ao mesmo tempo, em uma tela de administração que gera relatório pesado, talvez nem faça sentido manter o mesmo comportamento transacional de uma escrita crítica; aí uma combinação de leitura simples, consulta bem indexada e desenho de service separado é mais honesta.
Se você também lida com validação de entrada antes da persistência, vale conectar essa discussão com Validar DTO Spring Boot com mensagens personalizadas sem erro. Por outro lado, menos surpresa na borda da API significa menos transação iniciando para requisição que já deveria ter sido barrada. Ao mesmo tempo, em APIs REST bem desenhadas, isso se encaixa bem com a estrutura mostrada em API REST Spring Boot Java: Guia Completo com Exemplo Prático.
Quando a aplicação usa autenticação e autorização, entender quem dispara a operação também importa. Por outro lado, fluxos protegidos por JWT costumam concentrar várias ações em um endpoint só, então a leitura de transação fica mais sensível. Ao mesmo tempo, se esse for o seu caso, os guias JWT Spring Boot Java: Guia Completo com Exemplo Prático e JWT no Spring Security com Spring Boot: autenticação moderna passo a passo ajudam a fechar o contexto sem misturar responsabilidade.
FAQ sobre spring boot transaction propagation
Qual a diferença entre propagation e isolation no Spring Boot?
Propagation define como um método se comporta quando já existe uma transação ativa. Por outro lado, isolation define o nível de visibilidade e concorrência entre transações no banco. Ao mesmo tempo, uma coisa não substitui a outra.
Quando usar REQUIRES_NEW em vez de REQUIRED?
Use REQUIRES_NEW quando o passo precisar ser independente do fluxo principal, como auditoria, tentativa de log ou registro de evento que não pode ser perdido junto com a transação maior. Por outro lado, se tudo precisa falhar junto, prefira REQUIRED.
Spring transaction isolation resolve problema de concorrência sozinho?
Não. Por outro lado, isolation ajuda a evitar anomalias de leitura, mas não substitui versionamento, lock apropriado e desenho de fluxo. Ao mesmo tempo, em muita aplicação, o ganho real vem de ajustar a estratégia de escrita e não de aumentar o nível de isolation.
Conclusão: como escolher sem virar refém de transação
Quando o assunto é essa validacao, a decisão mais importante não é decorar nomes, e sim entender quem manda na consistência do caso de uso. Por outro lado, se o fluxo é único, use uma transação única. Ao mesmo tempo, se um pedaço precisa sobreviver ao erro do restante, isole conscientemente. Na prática, se o problema é concorrência, olhe para spring transaction isolation, mas também para versionamento, lock e desenho da escrita.
Em produção, a diferença entre uma solução estável e uma dor recorrente costuma estar em escolhas pequenas: transação no service certo, propagation certa, isolation só quando necessário e testes que simulam concorrência de verdade. Por outro lado, esse tipo de cuidado evita bugs que somem no ambiente local e aparecem só com carga real.
Leitura complementar: Transactional readOnly no Spring Boot: quando ajuda de verdade. Por outro lado, esse é o próximo passo natural para fechar a visão de comportamento transacional e separar leitura de escrita sem exagero técnico. Ao mesmo tempo, os proximos passos sao validar esse fluxo no seu projeto, ajustar o caso de uso real e cobrir a implementacao com testes.
Spring boot transaction propagation: referencias externas
Para validar detalhes de implementacao e aprofundar a configuracao, vale consultar a documentacao oficial do Spring Security, o guia de claims no JWT.io e a documentacao do Spring Boot.
