3

I have a service with a method, which is not annotated with @Transactional:

@Service
@RequiredArgsConstructor
public class MainService {

    private final ServiceA serviceA;    

    public void processData() {
        List<EntityA> list = serviceA.getList();
        list.forEach(item -> {
        try {
            serviceA.doSomeDbOperations(item);
        } catch(Exception e) {
            // some processing here
        } finally {
            // some processing and DB interactions here
        }
        })
    }

}

The goal is to roll back the changes happened in try block (serviceA.doSomeDbOperations(item)) if an exception is thrown. so I annotated this method in ServiceA with @Transactional:

@Service
public class ServiceA {
    // dependencies

    @Transactional
    public void doSomeDbOperations(EntityA item) {
        // some logic here
        repositoryA.save(item)
        serviceB.deleteSomething(input)
    }
}

serviceB.deleteSomething(input) could throw an exception:

@Service
public class ServiceB {
    // dependencies
    
    public void deleteSomething(EntityA item) {
        // some logic here
        if(condition) {
            Throw new RuntimeException();
        }        
    }
}

The problem is that when an Exception is thrown, the changes in try block are not rolled back and data is non-consistent. Any idea where the problem is?

MehdiB
  • 870
  • 12
  • 34
  • Could you please confirm that the ServiceA and ServiceB are also annotated with the @Service annotation? – cdr89 Jul 22 '22 at 08:25
  • [Rolling back a declarative transaction](https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html): "Checked exceptions that are thrown from a transactional method do not result in rollback in the default configuration" – Andrey B. Panfilov Jul 22 '22 at 08:29
  • @cdr89 yes they are. I updated the question. – MehdiB Jul 22 '22 at 08:30

1 Answers1

1

I guess in ServiceB you are using a checked Exception. In Spring the transactions are rolled back just on unchecked exceptions by default, so try throwing for example a RuntimeException in your ServiceB.deleteSomething() method.

In case you need a checked Exception there, you can do something like this in your ServiceA.doSomeDbOperations() method:

@Transactional(rollbackFor = Throwable.class)

Some references:

https://www.baeldung.com/java-checked-unchecked-exceptions

How to make Spring @Transactional roll back on all uncaught exceptions?

cdr89
  • 958
  • 9
  • 18
  • I updated the question again. I am actually throwing a RuntimeException. I also added the rollbackFor but the same problem exists. – MehdiB Jul 22 '22 at 08:44
  • @MehdiB if rollbackFor is included, it should work. what database are you running on? some db does not support transaction or not enabled. – Richard Jul 22 '22 at 09:03
  • I am using PostgreSQL 11.14 – MehdiB Jul 22 '22 at 10:19
  • @MehdiB, is your doSomeDbOperations() method really public or has another visibility? – cdr89 Jul 22 '22 at 12:57