大聰明教你學Java | Spring Boot 事務回滾

不肯過江東丶 2022-01-08 07:24:44 阅读数:678

明教 教你 java spring boot

前言

我們開發系統的時候經常會遇到一些關於交易的需求,交易的過程大多數都比較繁瑣(會包括修改庫存、修改餘額、記錄交易賬單等等步驟),這時候我們就不得不考慮其中的潜在風險了,比如我們在交易的過程中修改了庫存(庫存 -1),接下來需要進行支付操作,但是此時系統突然宕機或者網絡突然中斷,這也就導致我們無法完成整個交易流程,雖然用戶還沒付錢,但是我們的庫存變少了(商家肯定就不高興了),所以我們就需要用到事務回滾來解决上述的問題。

Spring Boot 事務回滾

我們有兩種方式可以實現事務回滾,第一種是自動回滾,第二種是手動回滾,這兩種實現方式大同小异,二者都需要使用 @Transactional 注解來實現事務回滾,下面直接上代碼,看看二者之間到底哪裏不一樣。

在接口實現類中有一個插入會員信息的方法,咱們就對這個方法進行改造,分別實現一下自動回滾和手動回滾

/** * 插入會員信息 * * @param cashierMember 會員信息 * @return 結果 */
@Override
public int insertCashierMember(CashierMember cashierMember)
{

cashierMember.setCreateTime(DateUtils.getNowDate());
cashierMember.setCreateBy(ShiroUtils.getLoginName());
SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
return cashierMemberMapper.insertCashierMember(cashierMember);
}

自動回滾

/** * 插入會員信息 * * @param cashierMember 會員信息 * @return 結果 */
@Override
@Transactional(rollbackFor = Exception.class)
public int insertCashierMember(CashierMember cashierMember)
{

cashierMember.setCreateTime(DateUtils.getNowDate());
cashierMember.setCreateBy(ShiroUtils.getLoginName());
SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
return cashierMemberMapper.insertCashierMember(cashierMember);
}

我們可以看到方法上增加了一個注解 @Transactional(rollbackFor = Exception.class) ,通過該注解可以對异常進行捕獲,當發生异常時就可以進行回滾,從而撤銷本次的入庫操作。

很多方法中都會用 try-catch 對异常進行處理,如果此時在 catch 中對可能出現的异常進行了處理,但是並沒有再手動拋出(throw)异常,Spring 則會認為該方法成功執行,也就不會進行回滾。
在這裏插入圖片描述
正解如下:

/** * 插入會員信息 * * @param cashierMember 會員信息 * @return 結果 */
@Override
@Transactional(rollbackFor = Exception.class)
public int insertCashierMember(CashierMember cashierMember)
{

try {

cashierMember.setCreateTime(DateUtils.getNowDate());
cashierMember.setCreateBy(ShiroUtils.getLoginName());
SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
return cashierMemberMapper.insertCashierMember(cashierMember);
}catch (Exception e){

System.out.println("方法出現异常:" + e);
//手動拋出异常
throw new RuntimeException();
}
}

P.S. 如果 try-catch 語句在 finally 語句塊中進行了 return 操作,那麼 catch 語句塊中手動拋出的异常也會被覆蓋,同樣不會自動回滾。

手動回滾

手動回滾的實現方式也非常簡單,只需要添加一句代碼即可實現

/** * 插入會員信息 * * @param cashierMember 會員信息 * @return 結果 */
@Override
@Transactional(rollbackFor = Exception.class)
public int insertCashierMember(CashierMember cashierMember)
{

try {

cashierMember.setCreateTime(DateUtils.getNowDate());
cashierMember.setCreateBy(ShiroUtils.getLoginName());
SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
return cashierMemberMapper.insertCashierMember(cashierMember);
}catch (Exception e){

System.out.println("方法出現异常:" + e);
//實現手動回滾
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return 0;
}

P.S. 這裏只是舉個例子,手動回滾語句不一定要添加在 catch 代碼塊中,我們可以在任何一個地方使用手動回滾語句。需要注意的是,我們雖然可以在其他地方增加手動回滾語句,但是手動回滾語句後的代碼還會繼續執行,所以不建議在非 catch 代碼塊中使用手動回滾語句。 如果非要這麼用的話,就一定要好好斟酌一下自己的業務邏輯是不是會有 BUG 了。

Spring Boot 事務回滾注意事項

這裏我們再簡單說幾句關於 Spring Boot 事務回滾中的注意事項:

  1. 想實現回滾,首先要保證 Spring Boot 開啟了事務(在啟動類上增加 @EnableTransactionManagement 注解開啟事務(其實 Spring Boot 默認就是開啟事務的),其次就是實現回滾的方法必須是 public 的。
  2. @Transactional(rollbackFor=Exception.class) 錶示的是該方法無論拋出什麼异常都會進行自動回滾;如果不加 (rollbackFor=Exception.class) 的話,則代錶了默認值,也就是只有當該方法拋出了非檢查型异常(RuntimeException)時才會進行回滾。
  3. 由於事務的四大特性(原子性、一致性、隔離性、持久性),所以 @Transactional 一般是要加在業務層(也就是接口實現類)中。
  4. 如果將 @Transactional(rollbackFor=Exception.class) 加在了接口實現類上,那麼這個類下的所有方法都將會被加上事務管理,即所有方法都會在自己出現异常時進行回滾操作。

小結

本人經驗有限,有些地方可能講的沒有特別到比特,如果您在閱讀的時候想到了什麼問題,歡迎在評論區留言,我們後續再一一探討‍

希望各比特小夥伴動動自己可愛的小手,來一波點贊+關注 (*◡‿◡) 讓更多小夥伴看到這篇文章~ 蟹蟹呦(●’◡’●)

如果文章中有錯誤,歡迎大家留言指正;若您有更好、更獨到的理解,歡迎您在留言區留下您的寶貴想法。

你在被打擊時,記起你的珍貴,抵抗惡意;
你在迷茫時,堅信你的珍貴,拋開蜚語;
愛你所愛 行你所行 聽從你心 無問東西

版权声明:本文为[不肯過江東丶]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201080724444296.html