🧠 Spring @Transactional – Why Your Rollback Didn’t Happen This is one of the most dangerous “it works… until it doesn’t” scenarios in Spring. You mark a method with @Transactional. You throw an exception. You expect a rollback. But nothing rolls back. And you don’t notice… until production. ❌ The Problem: Self-Invocation In Spring, @Transactional works through proxies. When a method inside the same class calls another @Transactional method using this.method(), 👉 it bypasses the proxy. 👉 no interception happens. 👉 no transactional behavior is applied. In the image above: updateUser() calls this.sendEmail() That internal call never goes through Spring’s proxy. If sendEmail() fails? The save operation may NOT roll back. Silent failure. No warning. ✅ The Fix: Go Through the Proxy Instead of calling the method directly: Use self-injection Or move the transactional logic to a separate service When you call through the injected proxy (self.sendEmail()), Spring can properly intercept the call and apply transactional behavior. Now your rollback works as expected. 🎯 The Real Lesson Transactions are not just annotations. They are proxy-driven runtime behavior. If you don’t understand how Spring AOP works under the hood, you’ll eventually debug this at 2 AM. Have you ever been bitten by self-invocation in Spring? Let’s hear your story 👇 #Java #SpringBoot #SpringFramework #Transactional #SoftwareArchitecture #CleanCode #JavaDevelopment #BackendDevelopment
If send email is saving the row in db and scheduler will wake up and use the row and send it, then we can say it is transactional.
helpful
Excellent guide and concept, however it is conceptually somewhat flawed as email is not something that is transactional at a db level. I know that in this day and age, we can recall messages in Outlook, for instance, but this is not technically a rollback per se, because once the mail gets to the smtp server, it's gets delivered regardless. Message recall in and of itself is not a transactional rollback.