1

Here's the basic structure of the code in which I am facing the issue.

@Transactional
void A(){
    B();
    // Can I identify transaction rollback here?
    APICall(); // THIS SHOULD NOT BE CALLED IF THE TRANSACTION FAILS
}


void B(){
   try{
       C() // throws Null Pointer Exception
    }catch(Exception e){
        // logs the details
    }
}

@Transactional
void C(){
   D() // throws Null Pointer Exception
}

The entire problem is that APICall() in method A(). That API shouldn't be called if the transaction fails. Since C() doesn't catch the exception thrown by D(), the transaction (method C()) is rolled back and hence the outer transaction (method A()) will also be marked for the roll back. But since the method B() catches the NullPointerException thrown by C(), the execution of the instructions continues in method A() and APICall() method is called. But the transaction of method A() will be rolled back since the internal transaction was rolled back.

So is there a way to check if the transaction of method A() is marked for a rollback inside the method A() itself? So that I can prevent the APICall? Or the only way is to rethrow the caught exception in method B()?

SevenEli
  • 43
  • 3
  • You can return boolean from B() to know was method execution successful or not. and return from A if it was not. Or, catch exception in A() instead. – gneginskiy Feb 13 '23 at 10:14
  • @gneginskiy This is just the basic structure of the real code. In the real code, there are a lot of Bs. And in both the solutions that you have provided, I would have to change all those Bs. That's why I was looking for a solution where I can simply check if the transaction has been marked for rollback in A itself so that I can solve this by changing just one method. – SevenEli Feb 13 '23 at 10:32
  • If you catch the exception in B() and not throws it, you gonna need to control the flow with some kind of value returned from B(), a boolean value for example, and check in each nested method the value returned, so you can rollback the transaction. This is totally weird and gonna make your code smell, don't do that, refactor your code to throws an exception. – Fabio Miranda Feb 13 '23 at 10:38

1 Answers1

1

With your current approach, I see several issues.

  • Exception in B() is consumed, which is bad. why consuming exception is bad
  • Exceptional case is not handled properly in A() (when there is an exception in B()), which is breaking the whole purpose of Exceptions
  • As you have multiple B() methods, handling response from all of them in a single A() would look very ugly.

Why do you want to avoid using Exceptions for the very same reason they are built for? This situation calls for exception since the exception would not only provide you with break of flow, it will also provide you complete stack trace and you will be able to handle the situation in a much better way.

Aakash
  • 2,029
  • 14
  • 22