如何在JPA中强制 @Transactional 在提交两个仓库操作,而不仅仅是最后一个?

huangapple go评论72阅读模式
英文:

How to force @Transactional in JPA to commit two repository action not only just the last one?

问题

你好,我试图实现的目标是在使用Spring Data JPA和@Transactional注解的情况下执行"UPDATE"操作,然后执行"DELETE"操作,我希望这两个操作都能执行,但如果其中一个操作失败,我需要回滚,我应该如何正确地实现这一点?

首先,这是我的服务类:

@Transactional
@Service
public class TransDeliveryPlanningService {

    public ResponseRequest<TransDeliveryPlanning> deleteTransDeliveryPlanning(InputDeleteRequest<ViewAndDeleteRequest> request) {
        // ... 你的代码 ...

        TransDeliveryPlanning updatedTransDeliveryPlanning = transDeliveryPlanningRepository.save(transDeliveryPlanningOld);

        transDeliveryPlanningRepository.delete(transDeliveryPlanningOld);

        // ... 你的代码 ...

        return response;
    }
}

正如你所看到的,我在**transDeliveryPlanningRepository.save后面执行了transDeliveryPlanningRepository.delete,然后需要执行的下一个仓库操作是transStuffingRepository.deleteAll**。

我需要先保存再删除的目的是使用Hibernate Audit Envers来创建AuditLog,所以我希望将删除原因记录到我的审计表中,然后再删除记录。但是当我使用**@Transactional注解时,transDeliveryPlanningRepository.save(更新)没有被执行,我的函数只执行了transDeliveryPlanningRepository.deletetransStuffingRepository.deleteAll,我如何确保使用@Transactional**注解时仍然执行保存操作呢?

更新

根据Morteza Bandi在下面的建议,这是我的更新后的代码:
这是我的仓库:

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

当我这样做时,仍然没有在删除之前执行更新操作,我还漏掉了什么吗?

英文:

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&lt;TransDeliveryPlanning&gt; deleteTransDeliveryPlanning(InputDeleteRequest&lt;ViewAndDeleteRequest&gt; request) {
        String currentUser = request.getLoggedInUser();
        String reasonDeleted = request.getReason();
        Long id = request.getObject().getId();
        ResponseRequest&lt;TransDeliveryPlanning&gt; response = new ResponseRequest&lt;TransDeliveryPlanning&gt;();

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

        transDeliveryPlanningRepository.delete(transDeliveryPlanningOld);

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

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

        //NOTE end of delete transStuffing

        if(updatedTransDeliveryPlanning != null) {
            response.setResponse(&quot;Sukses Hapus&quot;);
            response.setObject(updatedTransDeliveryPlanning);
        } else {
            response.setResponse(&quot;Gagal Hapus&quot;);
        }
        
        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&lt;TransDeliveryPlanning, Long, Integer&gt;, JpaRepository&lt;TransDeliveryPlanning, Long&gt; {
    
    @Modifying
    TransDeliveryPlanning save(TransDeliveryPlanning transDeliveryPlanning);
}

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

答案1

得分: 1

尝试将 @Transactional 放在 service 方法内的一个方法上方。例如:

@Service
public class MyService {

   @Autowired
   private MyRepo myRepo;

   @Autowired
   private MyRepo2 myRepo2;

   @Transactional
   public void yourTransactionService(arguments){
      *** 在这里进行您的 DML 操作 ***
      例如 
      myRepo.save(blabla);
      myRepo.delete(blabla1);
      ...
      myRepo2.save(blabla2);
      myRepo3.delete(blabla3);
      ...
   }

}

这样,在调用 yourTransactionService(arguments) 时,要么所有的 DML 操作都被持久化,要么它们都不被持久化。

英文:

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.

答案2

得分: -2

有一种简单的方法可以通过使用查询本身来实现这一点,您可以使用commit、transaction和rollback。

例如:您首先必须启动事务,然后需要将autocommit设置为0,这样每次发生的事务在您确认一切正常后将不会被提交,否则您可以传递一个语句作为回滚,这会将您在启动事务后执行的所有操作都回滚。为了更好地理解这一点,请使用以下[链接] 1

英文:

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

huangapple
  • 本文由 发表于 2020年10月13日 16:41:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/64331680.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定