1
votes

I have the following service layout of nested transactions:

@Component
public class Main implements RPCInterface {

  @Autowired
  private ServiceA serviceA;

  @Autowired
  private ServiceB serviceB;

  @Autowired
  private ServiceC serviceC;

  @Override
  @Transactional (value="txManager", propagation=Propagation.REQUIRED, rollbackFor={ExceptionOne.class, ExceptionTwo.class, ExceptionThree.class})
  public void outerMethod() throws ExceptionO {

    try {
      serviceA.methodA();
      serviceB.methodB();
      serviceC.methodC();

    } catch (ExceptionOne e) {
      throw new ExceptionO(e.getMessage, e);
    } catch (ExceptionTwo e) {
      throw new ExceptionO(e.getMessage, e);
    } catch (ExceptionThree e) {
      throw new ExceptionO(e.getMessage, e);
    }
  }
}

@Service
public class ServiceA implements SA {

  @Autowired
  private ServiceA1 serviceA1;

  @Override
  public void methodA() {
    serviceA1.methodA1();
  }
}

@Service
public class ServiceA1 implements SA1 {
  @Autowired
  private ServiceDBTable1 serviceDBTable1;

  @Autowired
  private ServiceA1A serviceA1A;

  @Transactional
  @Override
  public void methodA1() {
    serviceDBTable4.callToMapper4();
    serviceA1A.methodA1A();
  }
}

@Service
@Transactional (value="txManager", propagation=Propagation.REQUIRED)
public class ServiceA1A implements SA1A {

  @Autowired
  private ServiceDBTable2 serviceDBTable2;

  @Override
  public void methodA1A() {
    serviceDBTable1.callToMapper1();
  }
}

@Service
public class ServiceB implements SB {

  @Autowired
  private ServiceDBTable3 serviceDBTable3;

  @Override
  @Transactional (value="txManager", propagation=Propagation.REQUIRED)
  public void methodB() {
    serviceDBTable3.callToMapper3();
  }
}

@Service
public class ServiceC implements SC {

  @Override
  public void methodC() throws ExceptionThree {
    // code that throws ExceptionThree
  }
}

I need to make all the DB calls within ServiceA and ServiceB nested calls to rollback when ServiceC#methodC() throws an exception (or any of them for that matter that throws an exception -- ServiceA or ServiceB).

I tried to make Main#outerMethod transactional with REQUIRED propagation, but it seems like the database commits are not being rolled back. I have even specified the specific classes with rollbackFor but the commits persist. Does anyone know how to fix this issue?

2
Why don't you put the innerMethodThree into the same transaction as innerMethod_1a and 2a? - Simon
@Simon How do I accomplish that? innerMethodThree must be called after 1) and 2). - NuCradle
set the @Transactional on the outerMethod and do not require new for 1a and 2a - Simon
@Simon I updated the question with a more concrete example. I removed @Transactional from ServiceA1.methodA1() and ServiceB.methodB() and placed it on Main#outerMethod(), but the rollbacks are not occurring. - NuCradle
I mean you can add the ExceptionO in the rollbackFor clause - Simon

2 Answers

2
votes

What I did to make it work was to migrate ServiceB.methodB() and ServiceC.methodC() calls to ServiceA.methodA(), and make methodA() @Transactional while throwing all my exceptions from methodA() and rollback based on those three exceptions (my logic actually allowed me to do that):

@Component
public class Main implements RPCInterface {

  @Autowired
  private ServiceA serviceA;

  @Override
  public void outerMethod() throws ExceptionO {

    try {
      serviceA.methodA();

    } catch (ExceptionOne e) {
      throw new ExceptionO(e.getMessage, e);
    } catch (ExceptionTwo e) {
      throw new ExceptionO(e.getMessage, e);
    } catch (ExceptionThree e) {
      throw new ExceptionO(e.getMessage, e);
    }
  }
}

@Service
public class ServiceA implements SA {

  @Autowired
  private ServiceA1 serviceA1;

  @Autowired
  private ServiceB serviceB;

  @Autowired
  private ServiceC serviceC;

  @Override
  @Transactional (value="txManager", propagation=Propagation.REQUIRED, rollbackFor={ExceptionOne.class, ExceptionTwo.class, ExceptionThree.class})
  public void methodA() throw ExceptionOne, ExceptionTwo, ExceptionThree {
    serviceA1.methodA1();
    serviceB.methodB();
    serviceC.methodC();
  }
}

@Service
public class ServiceA1 implements SA1 {
  @Autowired
  private ServiceDBTable1 serviceDBTable1;

  @Autowired
  private ServiceA1A serviceA1A;

  @Transactional
  @Override
  public void methodA1() {
    serviceDBTable4.callToMapper4();
    serviceA1A.methodA1A();
  }
}

@Service
@Transactional (value="txManager", propagation=Propagation.REQUIRED)
public class ServiceA1A implements SA1A {

  @Autowired
  private ServiceDBTable2 serviceDBTable2;

  @Override
  public void methodA1A() {
    serviceDBTable1.callToMapper1();
  }
}

@Service
public class ServiceB implements SB {

  @Autowired
  private ServiceDBTable3 serviceDBTable3;

  @Override
  @Transactional (value="txManager", propagation=Propagation.REQUIRED)
  public void methodB() {
    serviceDBTable3.callToMapper3();
  }
}

@Service
public class ServiceC implements SC {

  @Override
  public void methodC() throws ExceptionThree {
    // code that throws ExceptionThree
  }
}
0
votes

Since there is no code presented it is hard to know for sure. However, transactions only work when methods are public. Private methods are not proxied and hence transaction support for them is not there.

Read through Declarative Transations - Spring Docs for more details.

Please post code if you still are struggling for getting better help.