Search code examples
springspring-boottransactionsspring-transactions

Spring boot transaction management does not work for following configuration


I have following configuration in my spring boot application. and I am throwing RuntimeException from Repository layer and Service layer to see transaction handling. When I throw RuntimeException from repository layer, the transaction is getting rolled back, however, if I throw Runtime exception from Service layer as shown below, the transaction is not getting rolled back and data is saved into the database. Could anybody help me what is wrong with the following configuration?

@Configuration
@SpringBootApplication
@EnableAsync
@EnableScheduling
@ComponentScan(basePackages = { "com.abc.xyz.api.config", Constant.BASE_PACKAGE, com.abc.c4s.util.Constant.BASE_PACKAGE })
@EnableAutoConfiguration(exclude = { JndiConnectionFactoryAutoConfiguration.class, DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class })
@PropertySources({
    @PropertySource(value = "classpath:jdbc.properties")
})
@EnableTransactionManagement
public class ProjectManagerApplication {

    public static ConfigurableApplicationContext context;

    private static final Logger logger = LoggerFactory.getLogger(ProjectManagerApplication.class);

    public static ConfigurableApplicationContext startServer() throws Exception {

        logger.debug("Starting server");

        SpringApplicationBuilder builder = new SpringApplicationBuilder(ProjectManagerApplication.class);
        builder.headless(false);


        context = builder.run(new String[] {});

        logger.debug("Server started - " + (context != null ? context.isRunning() : "context null, starting failed"));

        return context;

    }
}

And, Following is my controller class configuration

@RestController
public class RWSettingsController {

    @Autowired
    SettingsService rwSettingsService;

    @RequestMapping(value = "/api/settings", method = RequestMethod.POST)
    public void createKiosk(@RequestBody SettingsDTO settingDTO) throws ABCInternalException, ABCException {
                //try{
                    settingsService.create(settingDTO);
                //}catch (Exception e){
                //    e.printStackTrace();
               // }
    }



}

This is my service class.

@Service
@Transactional//(propagation = Propagation.SUPPORTS, readOnly = true)
public class SettingsService {

    @Autowired
    SettingsRepository settingsRepository;

    @Autowired
    ModelMapper modelMapper;

    private static final Logger logger = LoggerFactory.getLogger(SettingsService.class);

//    @Transactional(
//            propagation = Propagation.REQUIRED,
//            readOnly = false)
    public void create(SettingsDTO settingsDTO) throws ButterflyInternalException, ButterflyException {
        try{

            SettingsModel settingsModel = new SettingsModel ();
            settingsModel .setActive(true);
            settingsModel .setParameterDescription(settingsDTO.getParameterDescription());
            settingsModel .setParameterName(settingsDTO.getParameterName());
            settingsModel .setParameterValue(settingsDTO.getParameterValue());

            //throw new HibernateException("");

            rwsSettingsRepository.create(rwsKioskSettingsBPA);
            throw new RuntimeException("");

        }catch(ABCException e){
            logger.error(e.getMessage(),e);
            throw e;
        }catch(Exception e){

            logger.error(e);
            throw e;

        }


    }

}

And my repository class is as follows

@Repository
//@Transactional//(propagation = Propagation.SUPPORTS, readOnly = true)
public class SettingsRepository {

    @Autowired
    @Qualifier("sessionFactory")
    SessionFactory abcSessionFactory;

    @Autowired
    ABCUtilityRepository abcUtilityRepository;

    private static final Logger logger = LoggerFactory.getLogger(SettingsRepository .class);

//    @Transactional(
//            propagation = Propagation.REQUIRED,
//            readOnly = false)
    public void create(RWSKioskSettingsBPA rwsKioskSettings) throws ABCException, ABCInternalException {
        abcUtilityRepository.saveEntity(rwsKioskSettings);
//throw new RuntimeException();


    }

Solution

  • I found what was the reason behind this behavior. Spring boot rolls back the transaction if application throw runtime exception. In my service layer, I catch the runtime exception and throw the application specific exception which is the reason why spring does not roll back the transaction. So after changing the code to throw the same runtime exception that was originally generated, application successfully rolled back the transaction.

    Above code will run if I put this in the service layer @Transactional(rollbackFor = Exception.class)