javaspring-bootspring-transactions

Spring boot @Transactional not rolling back the database inserts


Need some help here, I'm not able to understand why my transactions are not getting rolled back in an event of exception.

I will try to put my code as close to as It is on the project (cannot share on the internet)

This is my Service

@Service
@SL4j
@Transactional(propagation = propagation.SUPPORTS, readOnly=true)
public class PublicationServiceImpl implements PublicationService{
    @Autowired 
    private PartnerRepo partnerRepo;
                
    @Autowired
    private FlowRepo flowRepo;

    @Autowired
    private PubRepo pubRepo;
                
    @Override
    @Transactional(propagation = propagation.REQUIRED, rollbackFor=Exception.class)
    public int save(Request request) {
    try{
                   int  pk_id_partner = partnerRepo.save(request);
                   int  pk_id_flow = flowRepo.save(request);
        
                   String publicationCode = generatePubCode(request);
                   int publicationCode= pubRepo.save(pk_id_partner, pk_id_flow, request);
        }
        catch(Exception e){
        log.error("Exception in saving");
    }
    return 0;
    }
}

This is my Repository (example of 1 , all 3 repos follow same coding standards)

@Repository
@Slf4j
public class PartnerRepo implemets PartnerRepo{
    @Autowired
    private NamedParamaterJDBCTemplate namedParamaterJDBCTemplate;
    //String Declarations .....

    private MapSqlParameterSource sqlParameterSource;

    @Override
    public int save(Request request){
        sqlParamatersSource = new MapSqlParameterSource();
        //sqlParamatersSource.addValue(.....)
        //sqlParamatersSource.addValue(.....)
        //sqlParamatersSource.addValue(.....)

        return executeQuery();
    }

    private int executeQuery(){
        try{
            keyHolder = new GenerateKeyHolder();
            namedParamaterJDBCTemplate.update(getInsertQuery(), sqlParamaterSource , kekHolder, new String[]{"pk_id"})
            return keyHolder.getKey().intValue();
        }catch(Exception e){
            log.error("Exception while saving");
            return 0;
        }
    }
}

So the problem is , Consider there is an exception in the method generatePubCode(request); , ideally since I have used @Transactional at class level and method level , The previous 2 repo transactions () should be rolled back right? However it isn't happening, Even After the code is finished execution I can see the records in DB (Postgres DB v10).

Please help figure out this issue , Am I doing something fundamentally wrong ?

Please do let me know in case you need further information that might help here!

P.S: I have tried all permutations of @Transactional , nothing works : ONLY having this in the catch block works! TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); I wonder If its the right approach for a springBoot project

Thanks in advance for the help!

Edit: as per suggestion made the PublicationServiceSaverImpl.save() public

Best reagards, Bhargav.


Solution

  • There are several things that break proper transactions in Spring

    1. Your service method is private
    2. You are catching and swallowing exceptions

    private method

    The fact that your PublicationServiceImpl save method is private basically makes the @Transactional on that method useless. As a private method cannot be proxied, no transactions will apply. Even if it would be public it wouldn't work as you are calling the method from within the same object, hence the transactionality of that method applies.

    To fix, make your method public and call the save method from an other class (or make the actual method that is calling save have the proper @Transactional.

    The fact that is doesn't work is due to the type op AOP being used, by default Spring will use proxies and this is a drawback of using proxy based AOP.

    Another solution to make it work with private methods is to switch to full-blown AspectJ with either compile-time or load-time weaving of the classes. Both require additional setup and that can be tedious.

    Catch and swallow exceptions

    You have in both your repository as well as your service a try/catch block. Each of those catches and swallows the exceptions (they are logged but not re-thrown).

    For transactions to work properly it needs to see the exceptions. The fact that you are catching and swallowing them, makes the transaction aspect not see them and instead of doing a rollback, do a commit. For the transaction aspect everything is ok because there was no exception.

    To fix, remove either the try/catch or rethrow the exceptions.