Entrevista Spring Boot: Propagação de Transações
Domine a propagação de transações no Spring Boot: REQUIRED, REQUIRES_NEW, NESTED e mais. 12 perguntas de entrevista com código e armadilhas comuns.

A propagação de transações representa um conceito fundamental no Spring Boot, regularmente avaliado em entrevistas técnicas. Compreender como as transações interagem entre métodos anotados com @Transactional ajuda a evitar bugs sutis em produção e permite projetar arquiteturas robustas.
Os entrevistadores avaliam a capacidade de escolher o nível de propagação correto conforme o contexto de negócio. Saber explicar por que REQUIRES_NEW em vez de REQUIRED em um caso específico faz a diferença.
Fundamentos da propagação de transações
Pergunta 1: O que é propagação de transações no Spring?
A propagação define o comportamento de um método transacional quando é chamado dentro do contexto de uma transação existente. Ela responde à pergunta: "O que acontece quando um método @Transactional chama outro método também anotado?"
// Demonstrando o conceito de propagação
@Service
public class OrderService {
private final PaymentService paymentService;
private final OrderRepository orderRepository;
// Transação pai - inicia uma nova transação
@Transactional
public void createOrder(OrderRequest request) {
// Salva o pedido na transação atual
Order order = orderRepository.save(new Order(request));
// Chamada para outro método @Transactional
// A propagação determina: mesma transação ou nova?
paymentService.processPayment(order.getId(), request.getAmount());
}
}
@Service
public class PaymentService {
// Propagação padrão: REQUIRED
// Junta-se à transação existente de createOrder()
@Transactional
public void processPayment(Long orderId, BigDecimal amount) {
// Executa na MESMA transação que createOrder()
// Se este método falhar, o pedido também é revertido
}
}O Spring fornece sete níveis de propagação, cada um adequado a necessidades de negócio específicas. A escolha impacta diretamente a consistência dos dados e o desempenho.
Pergunta 2: Descreva o comportamento de REQUIRED (propagação padrão)
REQUIRED é a propagação padrão. Se uma transação existir, o método junta-se a ela. Caso contrário, uma nova transação é criada. É o comportamento mais comum e intuitivo.
// REQUIRED: comportamento padrão
@Service
public class UserService {
private final UserRepository userRepository;
private final AuditService auditService;
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser(Long userId, UserUpdateRequest request) {
// Inicia uma transação se nenhuma existir
User user = userRepository.findById(userId).orElseThrow();
user.setEmail(request.getEmail());
userRepository.save(user);
// A auditoria junta-se à mesma transação
auditService.logUpdate(userId, "EMAIL_CHANGED");
}
}
@Service
public class AuditService {
private final AuditLogRepository auditLogRepository;
@Transactional(propagation = Propagation.REQUIRED)
public void logUpdate(Long userId, String action) {
// Junta-se à transação de updateUser()
// Commit ou rollback juntos
auditLogRepository.save(new AuditLog(userId, action, Instant.now()));
}
}O diagrama abaixo ilustra o fluxo transacional:
updateUser() inicia TX-1
├── save(user) → TX-1
└── logUpdate() → junta-se a TX-1 (REQUIRED)
└── save(audit) → TX-1
Se logUpdate() falhar → rollback TX-1 → user E audit canceladosPergunta 3: Quando usar REQUIRES_NEW em vez de REQUIRED?
REQUIRES_NEW suspende a transação existente e cria uma nova transação independente. Útil quando uma operação precisa ser persistida independentemente do resultado da transação pai.
// REQUIRES_NEW: transação independente
@Service
public class PaymentService {
private final PaymentRepository paymentRepository;
private final PaymentAuditService auditService;
@Transactional
public void processPayment(Long orderId, BigDecimal amount) {
Payment payment = new Payment(orderId, amount);
paymentRepository.save(payment);
// Auditoria DEVE ser persistida mesmo se o pagamento falhar depois
auditService.logPaymentAttempt(orderId, amount);
// Simulando um erro após a auditoria
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new InvalidAmountException("Negative amount");
}
}
}
@Service
public class PaymentAuditService {
private final PaymentAuditRepository auditRepository;
// REQUIRES_NEW: faz commit independente da transação pai
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logPaymentAttempt(Long orderId, BigDecimal amount) {
// Nova transação TX-2 criada
// TX-1 (processPayment) está suspensa
auditRepository.save(new PaymentAudit(orderId, amount, "ATTEMPTED"));
// TX-2 faz commit aqui, independentemente de TX-1
}
}O fluxo transacional com REQUIRES_NEW:
processPayment() inicia TX-1
├── save(payment) → TX-1
├── logPaymentAttempt() → TX-1 SUSPENSA
│ └── inicia TX-2 → nova transação
│ └── save(audit) → TX-2
│ └── COMMIT TX-2 → audit persistido
│ └── TX-1 RETOMA
└── throw InvalidAmountException
└── ROLLBACK TX-1 → pagamento cancelado, mas audit preservadoREQUIRES_NEW pode causar deadlocks se a nova transação acessar os mesmos recursos bloqueados pela transação suspensa. Evite usar REQUIRES_NEW para modificar as mesmas tabelas que a transação pai.
Pergunta 4: Explique a propagação NESTED e como ela difere de REQUIRES_NEW
NESTED cria um savepoint dentro da transação atual. Se o método falhar, apenas as modificações desde o savepoint são revertidas, e não toda a transação pai.
// NESTED: savepoint dentro da transação pai
@Service
public class BatchProcessingService {
private final ItemRepository itemRepository;
private final ItemProcessor itemProcessor;
@Transactional
public BatchResult processBatch(List<Item> items) {
BatchResult result = new BatchResult();
for (Item item : items) {
try {
// Cada item é processado com um savepoint
itemProcessor.processItem(item);
result.addSuccess(item.getId());
} catch (ProcessingException e) {
// Rollback apenas deste item, não do batch inteiro
result.addFailure(item.getId(), e.getMessage());
}
}
return result; // Commit dos itens bem-sucedidos
}
}
@Service
public class ItemProcessor {
private final ItemRepository itemRepository;
// NESTED: cria um savepoint, rollback parcial possível
@Transactional(propagation = Propagation.NESTED)
public void processItem(Item item) {
item.setStatus("PROCESSING");
itemRepository.save(item);
// Validação de negócio
if (!isValid(item)) {
throw new ProcessingException("Invalid item");
// Rollback até o savepoint → apenas este item
}
item.setStatus("COMPLETED");
itemRepository.save(item);
}
}Comparação NESTED vs REQUIRES_NEW:
NESTED:
├── Usa um savepoint dentro da TX pai
├── Em caso de falha → rollback ao savepoint
├── Se a TX pai fizer rollback → NESTED também reverte
└── Mais performático (sem nova conexão)
REQUIRES_NEW:
├── Cria uma transação completamente independente
├── Em caso de falha → rollback apenas da TX filha
├── Se a TX pai fizer rollback → TX filha JÁ FEZ COMMIT
└── Requer uma nova conexãoA propagação NESTED requer suporte a savepoints JDBC. A maioria dos bancos modernos (PostgreSQL, MySQL, Oracle) suporta isso. Verifique a compatibilidade antes de usar.
Tipos de propagação avançados
Pergunta 5: Quando usar SUPPORTS e NOT_SUPPORTED?
SUPPORTS executa dentro da transação existente, se houver, caso contrário sem transação. NOT_SUPPORTED suspende qualquer transação existente e executa sem transação.
// SUPPORTS: transação opcional
@Service
public class ReportingService {
private final ReportRepository reportRepository;
// SUPPORTS: funciona com ou sem transação
// Útil para leituras que não precisam de garantias transacionais
@Transactional(propagation = Propagation.SUPPORTS)
public Report generateReport(Long reportId) {
// Se chamado de um método @Transactional → usa sua TX
// Se chamado diretamente → sem transação (leitura OK)
return reportRepository.generateComplexReport(reportId);
}
}
@Service
public class ExternalNotificationService {
private final ExternalApiClient apiClient;
// NOT_SUPPORTED: nunca dentro de uma transação
// Evita bloquear a TX durante uma chamada externa lenta
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendExternalNotification(String message) {
// TX pai suspensa durante a chamada
apiClient.send(message); // Chamada HTTP potencialmente lenta
// TX pai retoma depois
}
}// Exemplo de uso combinado
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final ReportingService reportingService;
private final ExternalNotificationService notificationService;
@Transactional
public void completeOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
order.setStatus("COMPLETED");
orderRepository.save(order);
// Geração do relatório na mesma TX (SUPPORTS)
Report report = reportingService.generateReport(orderId);
// Notificação externa FORA da transação (NOT_SUPPORTED)
// Evita timeout da TX se a API externa estiver lenta
notificationService.sendExternalNotification(
"Order " + orderId + " completed"
);
}
}Pergunta 6: Explique MANDATORY e NEVER
MANDATORY exige uma transação existente e lança uma exceção caso contrário. NEVER exige a ausência de transação e lança uma exceção se uma existir.
// MANDATORY: deve ser chamado de dentro de uma transação
@Service
public class AuditService {
private final AuditRepository auditRepository;
// MANDATORY: recusa-se a executar sem uma transação existente
// Garante que a auditoria é sempre atômica com a operação auditada
@Transactional(propagation = Propagation.MANDATORY)
public void logCriticalAction(String action, Long entityId) {
// Se chamado sem transação → IllegalTransactionStateException
auditRepository.save(new AuditLog(action, entityId, Instant.now()));
}
}
// CacheService.java
// NEVER: nunca deve estar em uma transação
@Service
public class CacheService {
private final CacheManager cacheManager;
// NEVER: o cache não deve participar de transações
// Evita inconsistências entre cache e BD após rollback
@Transactional(propagation = Propagation.NEVER)
public void invalidateCache(String cacheKey) {
// Se chamado de um @Transactional → IllegalTransactionStateException
cacheManager.getCache("entities").evict(cacheKey);
}
}// Uso correto de MANDATORY
@Service
public class SecurityService {
private final AuditService auditService;
@Transactional
public void changeUserPassword(Long userId, String newPassword) {
// Operação sensível...
updatePassword(userId, newPassword);
// A auditoria DEVE estar na mesma transação
// MANDATORY impõe esta restrição arquitetural
auditService.logCriticalAction("PASSWORD_CHANGE", userId);
}
// ERRO: chamada direta sem transação
public void badUsage() {
// Lança IllegalTransactionStateException porque não há TX
auditService.logCriticalAction("TEST", 1L);
}
}Pronto para mandar bem nas entrevistas de Spring Boot?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Armadilhas comuns em entrevistas
Pergunta 7: Por que @Transactional não funciona em chamadas internas?
Uma das armadilhas mais comuns. Chamadas internas a métodos (self-invocation) ignoram o proxy do Spring, desativando o gerenciamento transacional.
// ERRO CLÁSSICO: self-invocation
@Service
public class BrokenService {
private final ItemRepository itemRepository;
public void processItems(List<Long> itemIds) {
for (Long id : itemIds) {
// ARMADILHA: chamada interna → ignora o proxy
// @Transactional em processItem() é IGNORADO
processItem(id);
}
}
@Transactional
public void processItem(Long itemId) {
// Esta transação NUNCA é criada em chamadas internas
Item item = itemRepository.findById(itemId).orElseThrow();
item.setStatus("PROCESSED");
itemRepository.save(item);
}
}Soluções para evitar esta armadilha:
// Solução 1: Auto-injeção
@Service
public class FixedServiceWithSelfInjection {
private final ItemRepository itemRepository;
@Lazy
@Autowired
private FixedServiceWithSelfInjection self; // Auto-injeção
public void processItems(List<Long> itemIds) {
for (Long id : itemIds) {
// Chamada via proxy → @Transactional funciona
self.processItem(id);
}
}
@Transactional
public void processItem(Long itemId) {
// Transação corretamente criada
Item item = itemRepository.findById(itemId).orElseThrow();
item.setStatus("PROCESSED");
itemRepository.save(item);
}
}
// Solução 2: Separar em dois serviços (recomendado)
@Service
public class ItemOrchestrator {
private final ItemProcessor processor;
public void processItems(List<Long> itemIds) {
for (Long id : itemIds) {
// Chamada para outro bean → o proxy funciona
processor.processItem(id);
}
}
}
@Service
public class ItemProcessor {
private final ItemRepository itemRepository;
@Transactional
public void processItem(Long itemId) {
// Transação corretamente gerenciada
Item item = itemRepository.findById(itemId).orElseThrow();
item.setStatus("PROCESSED");
itemRepository.save(item);
}
}Pergunta 8: Como tratar exceções e rollback?
Por padrão, o Spring só faz rollback em RuntimeException e Error. Exceções checked NÃO disparam rollback automático.
// Comportamento de rollback conforme o tipo de exceção
@Service
public class TransactionRollbackDemo {
private final OrderRepository orderRepository;
// RuntimeException → ROLLBACK automático
@Transactional
public void methodWithRuntimeException() {
orderRepository.save(new Order());
throw new RuntimeException("Error"); // ROLLBACK
}
// Checked Exception → SEM rollback por padrão
@Transactional
public void methodWithCheckedException() throws IOException {
orderRepository.save(new Order());
throw new IOException("File error"); // Faz COMMIT mesmo assim!
}
// Forçar rollback em checked exception
@Transactional(rollbackFor = IOException.class)
public void methodWithRollbackFor() throws IOException {
orderRepository.save(new Order());
throw new IOException("Error"); // ROLLBACK graças a rollbackFor
}
// Excluir uma RuntimeException do rollback
@Transactional(noRollbackFor = BusinessException.class)
public void methodWithNoRollbackFor() {
orderRepository.save(new Order());
throw new BusinessException("Warning"); // COMMIT apesar da exceção
}
}Configuração recomendada para casos de negócio:
// Configuração transacional consistente
@Service
public abstract class BaseTransactionalService {
// Rollback em todas as exceções (checked e unchecked)
@Transactional(rollbackFor = Exception.class)
protected void executeInTransaction(Runnable operation) {
operation.run();
}
}
// PaymentService.java
@Service
public class PaymentService extends BaseTransactionalService {
private final PaymentRepository paymentRepository;
@Transactional(
rollbackFor = Exception.class,
noRollbackFor = InsufficientFundsException.class
)
public PaymentResult processPayment(PaymentRequest request) {
Payment payment = new Payment(request);
paymentRepository.save(payment);
if (request.getAmount().compareTo(getBalance()) > 0) {
// NÃO faz rollback - quer manter o registro da tentativa
throw new InsufficientFundsException("Insufficient balance");
}
return new PaymentResult(payment.getId(), "SUCCESS");
}
}Pergunta 9: Como o isolamento de transações funciona com a propagação?
Isolamento e propagação são complementares. O isolamento determina a visibilidade dos dados entre transações concorrentes.
// Combinando isolamento + propagação
@Service
public class IsolationDemo {
private final AccountRepository accountRepository;
// READ_COMMITTED: vê dados commitados por outras transações
@Transactional(
isolation = Isolation.READ_COMMITTED,
propagation = Propagation.REQUIRED
)
public BigDecimal getAccountBalance(Long accountId) {
// Pode ver valores diferentes se relido durante a transação
return accountRepository.findById(accountId)
.map(Account::getBalance)
.orElse(BigDecimal.ZERO);
}
// REPEATABLE_READ: garante a mesma leitura durante a transação
@Transactional(
isolation = Isolation.REPEATABLE_READ,
propagation = Propagation.REQUIRED
)
public void transferWithConsistentRead(Long fromId, Long toId, BigDecimal amount) {
Account from = accountRepository.findById(fromId).orElseThrow();
Account to = accountRepository.findById(toId).orElseThrow();
// Mesmo que outra transação modifique 'from' nesse meio tempo,
// sempre vemos o valor inicial (snapshot)
from.debit(amount);
to.credit(amount);
accountRepository.save(from);
accountRepository.save(to);
}
// SERIALIZABLE: isolamento máximo, sem concorrência
@Transactional(
isolation = Isolation.SERIALIZABLE,
propagation = Propagation.REQUIRES_NEW
)
public void criticalOperation(Long accountId) {
// Bloqueia qualquer outra transação sobre estes dados
// Usar com moderação - impacto no desempenho
Account account = accountRepository.findById(accountId).orElseThrow();
account.performCriticalUpdate();
accountRepository.save(account);
}
}Tabela resumo dos níveis de isolamento:
| Isolamento | Dirty Read | Non-Repeatable | Phantom |
|------------------|------------|----------------|---------|
| READ_UNCOMMITTED | Possível | Possível | Possível|
| READ_COMMITTED | Não | Possível | Possível|
| REPEATABLE_READ | Não | Não | Possível|
| SERIALIZABLE | Não | Não | Não |Quanto mais rigoroso o isolamento, mais o desempenho pode ser afetado por locks. SERIALIZABLE pode causar contenção significativa em produção de alto tráfego.
Padrões avançados
Pergunta 10: Como implementar o padrão "transactional outbox"?
O padrão outbox garante consistência entre as modificações no banco de dados e o envio de mensagens/eventos, mesmo em caso de falha.
// Padrão Transactional Outbox
@Service
public class OutboxService {
private final OutboxRepository outboxRepository;
// Salva o evento na mesma transação que a entidade de negócio
@Transactional(propagation = Propagation.MANDATORY)
public void saveEvent(String aggregateType, Long aggregateId, String eventType, String payload) {
OutboxEvent event = OutboxEvent.builder()
.aggregateType(aggregateType)
.aggregateId(aggregateId)
.eventType(eventType)
.payload(payload)
.status("PENDING")
.createdAt(Instant.now())
.build();
outboxRepository.save(event);
}
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final OutboxService outboxService;
@Transactional
public Order createOrder(OrderRequest request) {
// Criar o pedido
Order order = new Order(request);
orderRepository.save(order);
// Evento outbox na MESMA transação (MANDATORY)
// Se o commit der certo → ambos são persistidos
// Se houver rollback → nenhum é persistido
outboxService.saveEvent(
"ORDER",
order.getId(),
"ORDER_CREATED",
toJson(new OrderCreatedEvent(order))
);
return order;
}
}
// OutboxPublisher.java
// Processo separado que publica eventos
@Service
public class OutboxPublisher {
private final OutboxRepository outboxRepository;
private final MessageBroker messageBroker;
// Transação independente para cada publicação
@Scheduled(fixedDelay = 1000)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void publishPendingEvents() {
List<OutboxEvent> events = outboxRepository
.findByStatusOrderByCreatedAt("PENDING");
for (OutboxEvent event : events) {
try {
messageBroker.publish(event.getEventType(), event.getPayload());
event.setStatus("PUBLISHED");
event.setPublishedAt(Instant.now());
} catch (Exception e) {
event.setStatus("FAILED");
event.setError(e.getMessage());
}
outboxRepository.save(event);
}
}
}Pergunta 11: Como testar diferentes comportamentos de propagação?
Testes de propagação requerem atenção especial para verificar o comportamento transacional esperado.
// Testando comportamentos de propagação
@SpringBootTest
@Transactional
class TransactionPropagationTest {
@Autowired
private OrderService orderService;
@Autowired
private PaymentService paymentService;
@Autowired
private TestEntityManager entityManager;
@Test
void required_shouldShareTransaction() {
// Given
Order order = orderService.createOrder(new OrderRequest());
// When - pagamento na mesma transação (REQUIRED)
paymentService.processPayment(order.getId(), BigDecimal.TEN);
// Then - ambos visíveis antes do commit
entityManager.flush();
assertThat(entityManager.find(Order.class, order.getId())).isNotNull();
assertThat(entityManager.find(Payment.class, order.getId())).isNotNull();
}
@Test
void requiresNew_shouldCommitIndependently() {
// Given
Long orderId = null;
try {
// When - audit em REQUIRES_NEW
orderId = orderService.createOrderWithAudit(new OrderRequest());
throw new RuntimeException("Simulated failure after audit");
} catch (RuntimeException e) {
// Transação principal faz rollback
}
// Then - audit (REQUIRES_NEW) ainda está commitado
assertThat(findAuditLog(orderId)).isNotNull();
// Mas o pedido foi revertido
assertThat(findOrder(orderId)).isNull();
}
@Test
void mandatory_shouldThrowWithoutTransaction() {
// Given - nenhuma transação ativa
// When/Then - deve lançar uma exceção
assertThatThrownBy(() -> auditService.logCriticalAction("TEST", 1L))
.isInstanceOf(IllegalTransactionStateException.class)
.hasMessageContaining("No existing transaction");
}
}// Teste de integração com rollback e commit reais
@SpringBootTest
class PropagationIntegrationTest {
@Autowired
private OrderService orderService;
@Autowired
private OrderRepository orderRepository;
@Autowired
private AuditLogRepository auditLogRepository;
@Test
void nested_shouldRollbackOnlyNestedOnFailure() {
// Given
int initialCount = orderRepository.findAll().size();
// When - processamento batch com NESTED
BatchResult result = orderService.processBatchWithNested(
List.of(validItem(), invalidItem(), validItem())
);
// Then - apenas itens válidos são persistidos
assertThat(result.getSuccessCount()).isEqualTo(2);
assertThat(result.getFailureCount()).isEqualTo(1);
assertThat(orderRepository.findAll().size()).isEqualTo(initialCount + 2);
}
}Pergunta 12: Quais são as melhores práticas para configuração de transações?
Uma configuração transacional consistente em nível de projeto evita surpresas e facilita a manutenção.
// Configuração transacional centralizada
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
// Timeout padrão para todas as transações
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager tm = new DataSourceTransactionManager(dataSource);
tm.setDefaultTimeout(30); // 30 segundos máximo por transação
return tm;
}
}
// BaseService.java
// Anotações padrão para serviços
@Service
@Transactional(
readOnly = true, // Apenas leitura por padrão
rollbackFor = Exception.class // Rollback em qualquer exceção
)
public abstract class BaseService {
// Métodos de leitura herdam readOnly = true
}
// OrderService.java
// Serviço com configuração consistente
@Service
public class OrderService extends BaseService {
private final OrderRepository orderRepository;
// Herda readOnly = true
public Order findById(Long id) {
return orderRepository.findById(id).orElseThrow();
}
// Override para escritas
@Transactional(readOnly = false)
public Order createOrder(OrderRequest request) {
return orderRepository.save(new Order(request));
}
// Configuração explícita para casos críticos
@Transactional(
readOnly = false,
propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.SERIALIZABLE,
timeout = 10
)
public void criticalOperation(Long orderId) {
// Operação com isolamento máximo e timeout curto
}
}Checklist de melhores práticas:
Configuração de Transação - Checklist
✅ readOnly = true por padrão, override explícito para escritas
✅ rollbackFor = Exception.class para incluir checked exceptions
✅ Timeout adequado conforme o tipo de operação
✅ Evitar chamadas internas (@Transactional ignorado)
✅ REQUIRES_NEW apenas quando commit independente é necessário
✅ MANDATORY para garantir contexto transacional
✅ Testes explícitos de comportamentos de rollback
✅ Monitorar transações de longa duração
✅ Documentar escolhas de propagação não padrãoPronto para mandar bem nas entrevistas de Spring Boot?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Conclusão
A propagação de transações é um conceito fundamental avaliado em entrevistas Spring Boot. Pontos-chave a lembrar:
Tipos de propagação comuns:
- ✅ REQUIRED (padrão): junta-se ou cria uma transação
- ✅ REQUIRES_NEW: transação independente, commit separado
- ✅ NESTED: savepoint para rollback parcial
- ✅ MANDATORY: requer uma transação existente
Armadilhas a evitar:
- ✅ Self-invocation: ignora o proxy, @Transactional ignorado
- ✅ Checked exceptions: sem rollback por padrão
- ✅ REQUIRES_NEW nos mesmos dados: risco de deadlock
- ✅ Sem timeout: transações bloqueadas indefinidamente
Melhores práticas:
- ✅ readOnly = true por padrão
- ✅ rollbackFor = Exception.class sistematicamente
- ✅ Serviços separados para evitar self-invocation
- ✅ Testar explicitamente comportamentos de rollback
Dominar a propagação de transações demonstra compreensão profunda do Spring e do gerenciamento de dados. Esses conceitos são essenciais para projetar aplicações robustas e passar com sucesso em entrevistas técnicas.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Tags
Compartilhar
Artigos relacionados

30 Perguntas de Entrevista sobre Spring Boot: Guia Completo para Desenvolvedores Java
Prepare-se para suas entrevistas de Spring Boot com estas 30 perguntas essenciais sobre auto-configuração, starters, Spring Data JPA, segurança e testes.

Spring Modulith: Arquitetura de Monólito Modular Explicada
Aprenda Spring Modulith para construir monólitos modulares em Java. Arquitetura, módulos, eventos assíncronos e testes com exemplos em Spring Boot 3.

Entrevista Spring Batch 5: Particionamento, Chunks e Tolerância
Domine as entrevistas de Spring Batch 5: 15 perguntas essenciais sobre particionamento, processamento por chunks e tolerância a falhas com exemplos em Java 21.