1

With Spring, by default transactions use Propogation.REQUIRED, but this seems like a rather odd choice to me. If we ignore the existence of transactions, then it's a very normal pattern to catch an exception, and implement a fallback. E.g. in it's very basic form:

public void doSomethingFirst() {}
public void doSomethingElse() {}

public void doSomethingWithFallback() {
    this.doSomething();
    try {
        this.doSomethingElse();
    } catch (Exception e) {
        this.fallback();
    }
}

However, Propagation.REQUIRED breaks this:

public void doSomethingFirst() { }
@Transactional(propagation = Propagation.REQUIRED)
public void doSomethingElse() { }

@Transactional(propagation = Propagation.REQUIRED)
public void doSomethingWithFallback() {
    this.doSomethingFirst();
    try {
        this.doSomethingElse();
    } catch (Exception e) {
        this.fallback();
    }
}

Now if doSomethingElse fails, it will mark the transaction as rollback only. Even though we have a perfectly fine fallback, the entire transaction will be rolled back and there's nothing we can do to stop this.

The "fix" for this is to use NESTED instead of REQUIRED (at least in the doSomethingElse):

public void doSomethingFirst() { }
@Transactional(propagation = Propagation.NESTED)
public void doSomethingElse() { }

@Transactional(propagation = Propagation.NESTED)
public void doSomethingWithFallback() {
    this.doSomethingFirst();
    try {
        this.doSomethingElse();
    } catch (Exception e) {
        this.fallback();
    }
}

Now, we're using savepoints, and if doSomethingElse fails it will only rollback doSomethingElse.

To me it looks like NESTED is the behaviour that we almost always want, and I'm struggling to come up with any use case where REQUIRED would be preferred. REQUIRED prohibits the caller from recovering from errors, which is normally a bad idea.

But given that REQUIRED is the default, and that NESTED is barely ever used, surely there must be some reason why we should use REQUIRED over NESTED.

In what cases should we prefer REQUIRED over NESTED?

Tiddo
  • 6,331
  • 6
  • 52
  • 85
  • 1
    The `@Transactional` on the `doSomethingElse` is useless. Not every database supports savepoints, whereas all of them support `REQUIRED`. Next to that it is the default for transactions for EJBs as well and Spring has aligned with that since its inception. Also what you consider a basic practice (i.e. a fallback) isn't that basic or common. When you write something it either fails or succeeds, I don't want to do something else. If you really wanted you could do that in a new transaction instead of using savepoints as well. – M. Deinum Oct 26 '21 at 11:28
  • The `@Transactional` on `doSomethingElse` is definitely not useless. `doSomethingElse` might perform multiple operations that require a transaction, and might be used in more than one context. And in any sufficiently complex system, you'll definitely use fallbacks where appropriate. A new transaction has entirely different characteristics though, this is not a replacement for nested at all – Tiddo Oct 26 '21 at 11:50
  • Regardless though, I'm getting dragged in the details, but it's missing the point. The question is when `REQUIRED` should be preferred over `NESTED`, and in your comment you addressed this with DB support only. So let's rephrase it a little bit different: if your DB supports savepoints, why use REQUIRED over NESTED? – Tiddo Oct 26 '21 at 11:52
  • Your question is about the default, that has been answered because it follows the default EJB behavior and that not all dbs support this. Also what you think is a common usecase isn't so common as you think. Finally most projects want an all or nothing operation not an all or partial operation which you would get with NESTED. – M. Deinum Oct 26 '21 at 12:01

0 Answers0