A `@Transactional` method on an `OrderService` bean calls another `@Transactional` method on the same class via `this.otherMethod()`. In production, a failure partway through leaves the database in a partially-committed state that the second method's transactionality was supposed to prevent. What actually happened, and why doesn't `@Transactional` protect this call?
@Transactional's actual behavior — beginning and committing a transaction around the call — doesn't live inside the annotated method itself; it lives in a proxy object that Spring generates and wraps around the bean, and the proxy only intercepts calls that arrive from outside, through a reference to the proxy. A call written as this.otherMethod() from inside the class goes directly to the real object's own method, bypassing the proxy entirely, because this refers to the real, unproxied instance, not the wrapper Spring built around it. The annotation is still there, correctly written, but nothing is ever consulted to act on it for that specific call — it's as if @Transactional weren't present at all for a self-invocation. The fix is structural, not a different annotation: move the second method to a different bean and call it through an injected reference (which is the proxy from the caller's point of view), or, less cleanly, inject the bean's own proxy into itself and call through that.