Transaction is rolled back even though runtime exception is handled

246 views Asked by At

As you see the below code, I have handled the runtime exception and written some code to prevent this still transaction is rolled back to my surprise.

I have read up few answers on this similar question but still I end up getting the issue. Need clear explanation and solutions to tackle the issue please.

[Code Explanation]

In the below code, I am trying to save an obj of type Event(Version is unique key in my Events table) where it gives me unique constraint violation exception if already the same version is found. This behaviour of having an already existing version is not avoidable for my use case, however I can increase the version and reattempt it.

[Problem]

However what I observed is when it finds an existing version it goes to the catch block, it does increment the version and try reattempting and finally it becomes successful. method m2() is executed ultimately, so is m3() and finally m1().

But instead of getting a 201 response I am getting 500 error with message transaction is rolled-back.

What are the ways of handling this? thanks.

[Code]

@Transactional
void m1(Event event){

//DB write operation
m2(event);

//DB write operation
m3();

}

@Transactional
void m2(Event obj){

 boolean success = false;

 while(!success){

  try{

   eventRepo.save(event);
   success = true;

  } catch(UniqueConstraintViolationException e){

   event.setVersion(++event.getVersion());

   }

 }

}
 
1

There are 1 answers

2
HassanUsman On

In your case, even though you're catching the UniqueConstraintViolationException and reattempting the save operation, the transaction isn't committed if an exception was thrown, and that's why you're seeing a 500 error.

To handle this situation you can use a programmatic transaction approach. Here's how you can do it:

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Autowired
private PlatformTransactionManager transactionManager;

void m1(Event event) {
    // Create a new transaction definition
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

    // Start a new transaction
    TransactionStatus status = transactionManager.getTransaction(def);

    try {
        // DB write operation
        m2(event);

        // DB write operation
        m3();

        // Commit the transaction if everything is successful
        transactionManager.commit(status);
    } catch (Exception e) {
        // Rollback the transaction if an exception occurs
        transactionManager.rollback(status);
    }
}

void m2(Event event) {
    boolean success = false;

    while (!success) {
        try {
            eventRepo.save(event);
            success = true;
        } catch (UniqueConstraintViolationException e) {
            event.setVersion(++event.getVersion());
        }
    }
}

This way, you can handle exceptions and retries within the scope of your transaction without affecting the outer transaction and causing a rollback. Try it and let me know if you still face any issue.