2

I have code like so

@Transactional(propagation = Propagation.NESTED)
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class C1 {
...
   public void xxx() {
      try {
         obj.someMethod();
      } catch (Exception e) {
         C2.yyy();
      }
   }
}

public class C2 {
    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
    public void yyy() {
        ...
    }
}

My assumption is that when obj.someMethod(); throws a constraint violation exception C2.yyy() should still be able to save stuff to the DB.

However what I see is that when C2.yyy() is called Postgres reports

ERROR: current transaction is aborted, commands ignored until end of transaction block

Why should it do that? After all C2.yyy() should run in a different transaction that should be unaffected by the state of what happened in the invoking code. No?

Update

On further debugging here is what I found - let us say the call stack is like so

@Transactional(readOnly = false, propagation = Propagation.NESTED)
b1.m1()
       @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
       b2.m2()
              b3.m3()
                      b4.m4()

My assumption was that the DB code in m4() would execute in a new transaction because of the annotation on b2.m2(). But it seems the code in TransactionAspectSupport.java looks at the transaction annotation only on the current method and not on the stack. When it does not find any @transactional on m4() it assumes REQUIRES. Isn't this incorrect?

H1101
  • 183
  • 2
  • 8

1 Answers1

1

As answered here DatabaseError: current transaction is aborted, commands ignored until end of transaction block : "This is what postgres does when a query produces an error and you try to run another query without first rolling back the transaction".

Meaning that you 'obj' should run in its own transaction and roll back on exception.

Regarding the question in the update: REQUIRES_NEW always creates a new transaction, it is both documented and tested.

Community
  • 1
  • 1
Alexander
  • 2,761
  • 1
  • 28
  • 33
  • But the "other query" should being run in its own (new) transaction and consequently should be unaffected by the status of the previously running transaction. Isn't REQUIRES_NEW a guarantee of that? – H1101 Jun 24 '16 at 08:48
  • Good question. My understanding is that REQUIRES_NEW uses a new connection, so you are right, in theory your code should work... I would suggest to enable logging for 'org.springframework.transaction' and re-run your example. Or just place a breakpoint in AbstractPlatformTransactionManager.getTransaction(...) – Alexander Jun 24 '16 at 09:42
  • Do you use plain JDBC, Hibernate or anything else? – Alexander Jun 24 '16 at 09:54
  • One more question: are you sure the error message is due to c2.yyy() call? You don't rethrow the exception so the code after the call to c2.yyy() will try to use the broken connection. – Alexander Jun 24 '16 at 10:01
  • No Hibernate. Plain JDBC. I do rethrow the exception but the error happens before that - i.e. as soon as you try to do the first DB operation in yyy() even if it a select. I also noticed that Postgres tx_id remains the same (from postgres logs). [This post](http://dba.stackexchange.com/questions/81011/transactions-within-a-transaction) clarifies that Postgres does not support nested transactions. I don't know how this would even work without nested tx support. Perhaps based on savepoints! – H1101 Jun 24 '16 at 10:06
  • Yes, quite possible. My question is: does c2.yyy() executes on a new connection? Enabling logging for 'org.springframework.transaction' or placing a breakpoint in AbstractPlatformTransactionManager.getTransaction(...) could answer this. I can't explain why c2.yyy() would produce any error if it executes on a new connection. – Alexander Jun 24 '16 at 10:23
  • One more thing: how is your Spring configured? Is it using JDK dynamic proxy (Spring default) or CGLIB proxying? If you did not specify this, it will use JDK dynamic proxy, so you @Transactional annotation on C2.yyy() will be simply ignored! Try extracting C2Interface and access c2.yyy() via the interface instead in c1.xxx(). – Alexander Jun 24 '16 at 10:26
  • Alexander: I updated my original question with what I found. I am going via JDK dynamic proxy and via an interface. – H1101 Jun 27 '16 at 08:12