Saheb
Saheb

Reputation: 51

Rollback is not working for multiple insert

@Transactional annotation on a method inside the Application class is not rolling back insertions. However, @Transactional annotation on the service class(EmpService.java) method("insertEmp(Emp emp)") is working as expected.

Could someone please let me know why @Transactional working differently? Spring Boot version - 2.1.3.RELEASE with the h2 database. Please let me know if any additional information required.

@SpringBootApplication
@ComponentScan("org.saheb")
@EnableJpaRepositories("org.saheb.repo")
@EntityScan("org.saheb.vo")
@EnableTransactionManagement
public class SpringJpaTransactionApplication implements CommandLineRunner {
@Autowired
private EmpService empService;

public static void main(String[] args) {
    SpringApplication.run(SpringJpaTransactionApplication.class, args);
}

@Override
public void run(String... args) throws Exception {
    insertSingleBatch();
}
@Transactional(rollbackFor=RuntimeException.class, propagation=Propagation.REQUIRES_NEW)
public void insertSingleBatch() {
    try {
        Set<Emp> empSet = new LinkedHashSet<>();
        Dept dept = new Dept();
        dept.setDeptNo(10);
        empSet.add(new Emp("abc", "abc", dept));
        empSet.add(new Emp("xyz", "xyz", dept));
        empSet.add(new Emp("def", "def", dept));
        empSet.add(new Emp("pqrstu", "pqr", dept));// This will fail as max character allowed in 5 and should rollback all the insertion. But, first three records are getting saved in h2 database.
        empService.insertEmp(empSet);
    } catch (RuntimeException e) {
        System.out.println("Exception in batch1.." + e.getMessage());
    }
}
}

@Service
public class EmpService {
@Autowired
private EmpRepository empRepository;

//@Transactional(rollbackFor=RuntimeException.class, propagation=Propagation.REQUIRES_NEW)//This is working as expected as all the insertions are rolling back after failure of 4th insertion
public void  insertEmp(Set<Emp> empSet) {
    System.out.println("Inside insert");
    for (Emp temp : empSet) {
        Emp temp2 =empRepository.save(temp);
        System.out.println("inserted-->"+temp2.getFirstName());
    }
}
}

Upvotes: 0

Views: 1160

Answers (1)

Ken Chan
Ken Chan

Reputation: 90447

You are "self-invocation" a @Transactional method from the same bean which will not work .This behaviour is well explained in the docs at here and here (Search the keyword "self-invocation")

You can simply move the @Transactional method to another bean.Then inject this bean to its client bean and invoke this @Transactional method.

Or use the TransactionTemplate to execute it within a transaction:


   @Autowired
   private TransactionTemplate txTemplate;

   @Override
   public void run(String... args) throws Exception {
            txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            txTemplate.execute(status->{
                insertSingleBatch();
                return null;
            });

   }

Please note that TransactionTemplate will ignore the setting on the @Transactional and you have to configure it programatically.

Upvotes: 3

Related Questions