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); } }
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); } }
Q2: Does @Transactional roll back on every exception?
@Transactional public void processRefund(Order order) throws RefundException { paymentRepository.markRefunded(order); externalRefundService.notify(order); // throws RefundException }
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); }
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.