1. 程式人生 > >@Transactional事務出現異常不回滾的處理

@Transactional事務出現異常不回滾的處理

一、背景:

目前很多專案的事務處理都是利用Spring的註解式事務實現的(@Transactional)。 

在測試事務回滾的過程中發現如下現象:
 
throw new RuntimeException("xxxxxxxxxxxx"); 事務回滾
 
throw new Exception("xxxxxxxxxxxx");        事務沒有回滾 
 

二、關於spring事務使用說明:


基於Spring AOP的事務管理,即宣告式事務管理,預設是針對RuntimeException回滾,既預設只對RuntimeException()及其子類進行事務回滾;非執行時型別的異常預設是不會回滾的。
spring aop 異常捕獲原理:被攔截的方法需顯式丟擲異常,並不能經任何處理,這樣aop代理才能捕獲到方法的異常,才能進行回滾,預設情況下aop只捕獲 RuntimeException 的異常,但可以通過配置來捕獲特定的異常並回滾
換句話說在service的方法中不使用try catch 或者在catch中最後加上throw new runtimeexcetpion(),這樣程式異常時才能被aop捕獲進而回滾

三、依賴事務管理的業務程式碼中出現異常該如何處理?

spring xml配置處理
1、針對該業務程式碼進行封裝,二次丟擲RuntimeException型別的異常;

2、利用硬編碼的方式,藉助spring api對事務進行顯式的回滾;

3、在spring配置檔案中對rollback-for屬性賦值。Tip:該配置也可以直接加在註解上。

<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
     <tx:method name="*" rollback-for="com.wangcw.exception.XyzException"/>
   </tx:attributes>
 </tx:advice>


  同時,Spring配置檔案中也可以宣告出不進行回滾的異常。

<tx:advice id="txAdvice">
    <tx:attributes>
       <tx:method name="update*" no-rollback-for="IOException"/>
       <tx:method name="*"/>
    </tx:attributes>
 </tx:advice>

springboot處理方式
方案1.例如service層處理事務,那麼service中的方法中不做異常捕獲,或者在catch語句中最後增加throw new RuntimeException()語句,以便讓aop捕獲異常再去回滾,並且在service上層(webservice客戶端,view層action)要繼續捕獲這個異常並處理
方案2.在service層方法的catch語句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();語句,手動回滾,這樣上層就無需去處理異常

      @GetMapping("delete") 
      @ResponseBody 
      @Transactional 
      public Object delete(@RequestParam("id") int id){ 
          if (id < 1){
              return new MessageBean(101,"parameter wrong: id = " + id) ; 
           } 
           try { 
               //delete country
               this.countryRepository.delete(id);
               //delete city
               this.cityRepository.deleteByCountryId(id);
               return new MessageBean(200,"delete success");
           }catch (Exception e){
               logger.error("delete false:" + e.getMessage());
               TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
               return new MessageBean(101,"delete false");
           }
       }