I have 13 years of Java experience. I have also interviewed hundreds of developers at MNCs.
Two questions about @Transactional that catch even senior developers. Try answering before you scroll down.
Q1: If a non-transactional method calls a @Transactional method in the same class, does the transaction apply?
@Service
public class OrderService {
public void placeOrder(Order order) {
this.saveOrder(order);
}
@Transactional
public void saveOrder(Order order) {
orderRepository.save(order);
inventoryRepository.update(order);
}
}
Enter fullscreen mode Exit fullscreen mode
Answer: No.
Reason, in plain terms: when you add @Transactional, Spring does not add the transaction logic inside your class. Instead, it creates a second object that sits in front of your class — think of it as a wrapper. Other classes that call saveOrder() (through @Autowired) actually call this wrapper first. The wrapper starts the transaction, then calls your real method, then commits or rolls back.
This wrapper only exists outside your class. So when your own code calls this.saveOrder(order), it skips the wrapper completely and goes straight to the real method. No wrapper means no transaction logic. No rollback if inventoryRepository.update() fails.
This bug never shows up in tests. It only shows up in production, when a failure leaves your data half-updated.
Fix: call the method from a different class, or inject the wrapper of your own class:
@Service
public class OrderService {
@Autowired
private OrderService self; // this is the wrapper, not the raw object
public void placeOrder(Order order) {
self.saveOrder(order); // now it goes through the wrapper
}
@Transactional
public void saveOrder(Order order) {
orderRepository.save(order);
inventoryRepository.update(order);
}
}
Enter fullscreen mode Exit fullscreen mode
Q2: Does @Transactional roll back on every exception?
@Transactional
public void processRefund(Order order) throws RefundException {
paymentRepository.markRefunded(order);
externalRefundService.notify(order); // throws RefundException
}
Enter fullscreen mode Exit fullscreen mode
Answer: No.
Reason: by default, @Transactional only rolls back on unchecked exceptions (RuntimeException and its subclasses). RefundException above is a checked exception. So if it is thrown, Spring lets the transaction commit anyway — including the markRefunded write that happened right before the failure.
Fix: tell Spring to roll back on any exception, not just unchecked ones:
@Transactional(rollbackFor = Exception.class)
public void processRefund(Order order) throws RefundException {
paymentRepository.markRefunded(order);
externalRefundService.notify(order);
}
Enter fullscreen mode Exit fullscreen mode
I cover topics like this in more depth in my guide — Core Java, Java 8 to 21, Multithreading, Spring Boot, Microservices, Design Patterns, and Coding Round Patterns.
Free sample: https://drive.google.com/file/d/1u3PQbTY1gLn34UmWG7Cxx4cmdibD2dvU/view?usp=sharing
Full guide: https://kamaninikhil.gumroad.com/l/java-interview-guide
Got either one right? Drop it in the comments.
답글 남기기