Spring手動提交事務和回滾事務


  1. 背景介紹

  本文基於快遞包裹取件(用戶獲取包裹並將包裹信息存儲數據庫)和包裹入庫(快遞員將包裹放入收發室並將包裹信息存儲如數據庫)場景,並將包裹入庫信息和取件信息分別存入不同的數據庫。這樣當用戶取件時,需要更新兩個表信息(入庫表中的包裹狀態和取件表中插入取件信息)。

  2. 問題描述

  在采用SSM框架搭建后端服務時,若Service層業務邏輯較復雜,一條業務邏輯中可能會調用多個dao層更改數據庫的接口。此時若采用Spring自帶的@Transactional注解進行事務處理,將難以滿足業務需求。正如代碼塊1所示:

代碼塊1
packet com.example.pickup;
public interface PickUpDao{
    @Delete("delete from pickup where orderNum=#{orderNum}")
    public int removePickUpPacket(String orderNum)
}

packet com.example.storage;
public interface StorageDao{
    @Update("update storage set isPickup=false where orderNum=#{orderNum}")
    public int updateStatuOfStoragePacket(String orderNum,boolean statu)
}

packet com.example.pickup;
public class PickUpServiceImpl{
    @Autowired
    private PickUpDao pickUpDao;
    @Autowired
    private StorageDao storageDao;
    
    @Transactional(readOnly=false)
    public int addPickUpPacket(PickUp pickUp){
        boolean isAlreadyPickUp=pickUpDao.getPickUpPacket(pickup.getOrderNum())==null?false:true;
        if(isAlreadyPickUp){
            return 3;// 表示該包裹已經取件
        }
       	Integer res1=dao.addPickUpPacket(pickUp);
        if(res1!=0){
           Integer res2=storageDao.updateStatuOfStoragePacket(pickUp.getOrderNum(),true);
            if(res2!=-1){
                return 2;// 表示取件成功
            }else{
                return 0; //①
            }
        }
        return 0;// 入庫失敗(最大可能是數據庫異常,由統一異常處理解決)
    }
}

  如代碼塊1所示,在代碼行①的位置,更新入庫包裹狀態出錯(可能是因為包裹入庫信息被刪除)。此時程序應該回滾數據庫(包括對取件表的回滾)。但采用@Transactional注解注釋后的服務不會回滾數據庫,只會向Controller層返回相應的錯誤碼(如0表示數據庫入庫出錯),然后再由Controller層根據相應的錯誤碼進行數據庫刪除操作。若程序員操作不當(可能是忘記處理),會使得數據庫處於一種不一致狀態。為此,本文將采用一種更為靈活的事務管理方式—采用DataSourceTransactionManager進行手動事務管理。如代碼塊2所示:

代碼塊2
```java packet com.example.pickup; public class PickUpServiceImpl{ @Autowired private DataSourceTransactionManager transactionManager; TransactionStatus transactionStatus = null;
@Autowired
private PickUpDao pickUpDao;
@Autowired
private StorageDao storageDao;

public int addPickUpPacket(PickUp pickUp){
    transactionStatus=transactionManager.getTransaction(new 				DefaultTransactionDefinition());
    boolean isAlreadyPickUp=pickUpDao.getPickUpPacket(pickup.getOrderNum())==null?false:true;
    if(isAlreadyPickUp){
        return 3;// 表示該包裹已經取件
    }
   	Integer res1=dao.addPickUpPacket(pickUp);
    if(res1!=0){
       Integer res2=storageDao.updateStatuOfStoragePacket(pickUp.getOrderNum(),true);
        if(res2!=-1){
            ransactionManager.commit(transactionStatus);
            return 2;// 表示取件成功
        }else{
            transactionManager.rollback(transactionStatus);//回滾數據庫(兩張表均回滾)
            return 0;
        }
    }
    return 0;// 入庫失敗(最大可能是數據庫異常,由統一異常處理解決)
}

}


&emsp;&emsp;代碼塊2是采用<font face="Times New Roman">DataSourceTransactionManager</font>對代碼塊1中服務的改進。改進后的代碼將更加靈活,可根據具體情況對數據庫進行回滾。

#### &emsp;&emsp;3. 附錄:

&emsp;&emsp;采用<font face="Times New Roman">DataSourceTransactionManager</font>進行數據庫事務管理的代碼模板:

<center>代碼塊3</center>

```java
@Autowired
private DataSourceTransactionManager manager;

TransactionStatus status = null;

public void method(){
    status=manager.getTransaction(new DefaultTransactionDefinition());
    try{
      //dosomething
      manager.commit(transactionStatus);
    }catch(Exception ex){
      manager.rollback(transactionStatus);
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM