user3757432
user3757432

Reputation: 11

Transaction not rollbacked

I have a set of operation i would like to be rollbacked if there are an error.

My class

public class BSException extends RuntimeException{
...
}

public class saleFacade{

  public update(){

    for (){
      try{
        renewSale();
      }
      catch(BSException){
        logger.error();
      }
    }
  }

  @Transactional
  public renewSale(){
    try{
      findSale(); // read only Transactional
      xxx.renewSpecialSale();
     }
     catch(Exception e){
       logger.error(...);
     }
 }
}


public class xxx(){
  public void renewSpecialSale(){    
    payFee(); //write to db
    if(error){
      throw new BSException();
    }
  }

 @Transactional(propagation = Propagation.REQUIRED)
 public payFee(){
   try{
     ...
   }
   catch(BsException e){
     ...
   }
   catch(Exception e){
     ...
   }
 }
}

@Configuration
@EnableTransactionManagement
public class DBConfiguration{
  @Bean(name = "dataSource")
  public BasicDataSource dataSource(){
  ...
  }
}

Inn renewSpecialSale error is throw.

In the renewSale method, if there is an error, i would like to rollback. Right now nothing is rollbacked

any idea?

Upvotes: 1

Views: 229

Answers (4)

Nathan Hughes
Nathan Hughes

Reputation: 96454

If you catch the exception before it leaves the method, then there is no way the proxy wrapping the method can know that an exception was thrown.

Either remove the try-catch entirely or rethrow the exception so that the exception can leave the method that you marked @Transactional (and get intercepted by the proxy), and the rollback will take place.

I recommend removing exception-handling from all these methods. Set up a central exception handler so that anything thrown from the controllers gets caught and logged, and otherwise let exceptions get thrown.

Make sure each of these classes that does something transactional is annotated separately, if you annotate on the method-level then each method that does something transactional should be annotated. Calling a transactional method from a non-transactional method on the same object doesn't go through the proxy (the proxy intercepts only those calls coming in from outside the object, and only then if that method is marked as transactional) so it isn't part of a transaction (+1 to Peter's answer for pointing this out).

I have no idea what your error flag is doing, it seems odd. Spring services shouldn't have state. If you fix the exception-handling you shouldn't need error flags.

Upvotes: 2

Peter
Peter

Reputation: 793

The problem comes from your nesting of Method calls and the usage of @Transactional. By default Springs Transaction Management

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Spring Transaction Management

This means, that the call of

xxx.payFee()

is not surrounded by a transaction when it's called through

saleFacade.update() -> saleFacade.renewSale() -> xxx.renewSpecialSale()

As far as I got it, you have at least these options

  • Mark xxx.renewSpecialSale() as @Transactional
  • Mark saleFacade.update() as @Transactional
  • Mark both classes xxx and saleFacade @Transactional

Upvotes: 1

Nirav Prajapati
Nirav Prajapati

Reputation: 3005

remove try catch block from your method and put below line of code

 @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

use this line of code insted of

@Transactional(propagation = Propagation.REQUIRED)

In above code spring container handle whole transaction management and here if we provide rollback attribute it automatically manage if any kind of exception occur it will rollback your transaction

and make sure that you have entry of transaction in your configuration file as below

 <tx:annotation-driven />

and also

I hope it will sure help you

Upvotes: 0

Ankur Singhal
Ankur Singhal

Reputation: 26077

Also you can create your own custom exception class and throw it.

Just throw any RuntimeException from a method marked as @Transactional.

By default all RuntimeExceptions rollback transaction whereas checked exceptions don't. This is an EJB legacy. You can configure this by using rollbackFor() and noRollbackFor() annotation parameters:

@Transactional(rollbackFor=Exception.class)

This will rollback transaction after throwing any exception.

@Transactional(rollbackFor = MyCheckedException.class)
public void foo() {
    throw new RuntimeException();    
}

Upvotes: 0

Related Questions