0

Hi what i trying to achieve is to do "UPDATE" action and then do "DELETE" action with Spring Data JPA and @Transactional annotation, and i want both action is executed but if one of the action is failed, i need a rollback, how do i do this properly?

first here is my service class :

@Transactional
@Service
public class TransDeliveryPlanningService {

    public ResponseRequest<TransDeliveryPlanning> deleteTransDeliveryPlanning(InputDeleteRequest<ViewAndDeleteRequest> request) {
        String currentUser = request.getLoggedInUser();
        String reasonDeleted = request.getReason();
        Long id = request.getObject().getId();
        ResponseRequest<TransDeliveryPlanning> response = new ResponseRequest<TransDeliveryPlanning>();

        TransDeliveryPlanning transDeliveryPlanningOld = transDeliveryPlanningRepository.findById(id).orElseThrow(() -> new ResourceNotFound("Data "+ id +" not found!"));
        transDeliveryPlanningOld.setIsDeleted(true);
        transDeliveryPlanningOld.setReasonDeleted(reasonDeleted);
        transDeliveryPlanningOld.setModifiedBy(currentUser);
        
        TransDeliveryPlanning updatedTransDeliveryPlanning = transDeliveryPlanningRepository.save(transDeliveryPlanningOld);

        transDeliveryPlanningRepository.delete(transDeliveryPlanningOld);

        //NOTE delete transStuffing
        List<TransStuffing> transStuffing = transStuffingRepository.findBydeliveryPlanningId(transDeliveryPlanningOld.getId());
        Boolean deletePermit = false;
        for(TransStuffing ts : transStuffing) {
            if(ts.getStatus().equalsIgnoreCase("new")) {
                deletePermit = true;
            } else {
                throw new ResourceIsDelete("Stuffing " + ts.getStuffingNo() + " Status already changed, delete is not permited!");
            }
        }

        if(deletePermit){
            transStuffingRepository.deleteAll(transStuffing);
        }

        //NOTE end of delete transStuffing

        if(updatedTransDeliveryPlanning != null) {
            response.setResponse("Sukses Hapus");
            response.setObject(updatedTransDeliveryPlanning);
        } else {
            response.setResponse("Gagal Hapus");
        }
        
        return response;
    }

}

as you can see, i do transDeliveryPlanningRepository.save and then the next line i do transDeliveryPlanningRepository.delete and the next repo i need to execute is transStuffingRepository.deleteAll

The goal i need to do save before delete is i use Hibernate Audit Envers to create an AuditLog, so i want log the delete reason into my audit table and then delete the record. But when i use **@Transactional** annotation the transDeliveryPlanningRepository.save (update) not executed, my function just execute transDeliveryPlanningRepository.delete and transStuffingRepository.deleteAll how i keep the save executed with @Transactional annotation?

UPDATE

As Morteza Bandi answer below suggestion, here is my updated code : here is my repository :

@Repository
public interface TransDeliveryPlanningRepository extends RevisionRepository<TransDeliveryPlanning, Long, Integer>, JpaRepository<TransDeliveryPlanning, Long> {
    
    @Modifying
    TransDeliveryPlanning save(TransDeliveryPlanning transDeliveryPlanning);
}

when i do this, still it's not update before delete, what did i still miss here?

Ke Vin
  • 3,478
  • 11
  • 60
  • 91
  • If I correctly understood, you need first to update a record and then delete it and if both actions are OK then commit both of them, otherwise rollback? By seeing your code, it seems to me OK. Did you enabled the spring annotation transaction management? – Angelo Immediata Oct 20 '20 at 11:58
  • yes, exactly i do that because i need to create audit log data, which is i store the update event then the delete event, yes, i enabled it by adding it at top of my class, is that right implementation of @Transactional ? – Ke Vin Oct 22 '20 at 06:53
  • Well i'd prefer to put `@Transactional` on each method instead od Class. By enabling spring transaction management I mean try to add, on your spring configuration class, the annotation `@EnableTransactionManagement` – Angelo Immediata Oct 22 '20 at 07:51
  • ohh i didn't put that annotation and i don't have spring configuration class, or are u refer to main class which is annotate with @SpringBootApplication and i have to put @EnableTransactionManagement? – Ke Vin Oct 22 '20 at 07:59
  • Well I usually have a configuration class for DB (I'm old and I like having control :) ) and on this configuration class I add the annotation. I never tried to put it on the main class.. you may try – Angelo Immediata Oct 22 '20 at 08:01
  • This two repositories are belonging to the same datasource? – m4gic Oct 25 '20 at 19:39

2 Answers2

1

try putting @Transactional above a method inside service method. e.g.:

@Service
public class MyService {

   @Autowired
   private MyRepo myRepo;

   @Autowired
   private MyRepo2 myRepo2;
   

   @Transactional
   public void yourTransactionService(arguments){
      *** your DML operations here ***
      e.g. 
      myRepo.save(blabla);
      myRepo.delete(blabla1);
      ...
      myRepo2.save(blabla2);
      myRepo3.delete(blabla3);
      ...

   }

}

This way when calling yourTransactionService(arguments) either all the DMLs are persisted or none of them persisted.

Morteza
  • 642
  • 7
  • 17
  • it's the same result, in myRepo.save (updating data) won't be executed, the jpa just go straight executing myRepo.delete, any idea? – Ke Vin Oct 14 '20 at 01:57
  • do you have `@Modifying` above your save(update) method? – Morteza Oct 14 '20 at 06:35
  • nope, my .save(object) function is default JPA repository function, so i don't have @Modifying anotation in my repository interface – Ke Vin Oct 14 '20 at 07:42
  • I had the same problem and this answer helped me before: https://stackoverflow.com/questions/11881479/how-do-i-update-an-entity-using-spring-data-jpa#:~:text=Spring%2Ddata%2Djpa%20supports%20update,update%20User%20u%20set%20u. – Morteza Oct 14 '20 at 08:08
  • so i have to define own .save() function and use annotaion Modifying and Query then define my own update query? – Ke Vin Oct 14 '20 at 09:07
  • Yes, that solution works for sure. But, if you look at the implementation of `save` you see that if the entity already exists, it just `merge`s the entity, else it saves new record. And, I am not sure why it is not sometimes work as desired. However, now that I am looking at my project I solved it by overriding `save` method and putting `@Modifying` above it. I cannot remember why I did that but it works :) – Morteza Oct 14 '20 at 10:11
  • can u update your answer with the snippet code sir? i try to do so, but it's still not working, i will update my repository at my question – Ke Vin Oct 19 '20 at 04:24
-2

There is a simple way to do this just by using query itself, you can use commit, transaction and roll back

For ex: you have to start the transaction at first, then you need to set autocommit = 0 so that every transaction that will take place will not get commit once you are ok with everything give commit else you can pass a statement as rollback this rolls back all the operations you have done after starting the transaction. For better understanding this use the following link

  • can u help me with sample code? the link is for mysql, i am using JPA Hibernate which is i should avoid code in SQL query layer as many as i could, if the problem can solved by JPA, i should use JPA – Ke Vin Oct 19 '20 at 04:28