Spring 中的事務註解 @Transactional

@Transactional 特性

  1. @Transactional註解只能應用到 public 修飾符上,對其他修飾符不起作用,但不報錯。
  2. @Transactional 一般加到實現類或實現類方法上,不要加到接口或接口方法上。
  3. @Transactional僅僅對 unchecked 異常進行事務異常回滾;如果是 checked 異常則不進行異常回滾。
    • unchecked 異常一般爲錯誤或運行時異常
      • Error:Throwable 的子類。著名的 VirtualMachineError 是 Error 的子類之一,VirtualMachineError 還有 StackOverflowError、OutOfMemoryError 等子類。
      • RuntimeException:Exception 的子類,而 Exception 同樣是 Throwable 的子類。RuntimeException 下面包括 NPE,ClassCastException,ArithmaticException 等異常,這些錯誤的特點是不用被顯式地拋出或捕獲,開發者通過仔細檢查代碼可以避免這些異常。
    • checked 異常則爲編譯過程中的異常
      • 其他 Exception:除了 RuntimeException 之外其他的 Exception 的子類,包括 FileNotFoundException,IOException,SQLException 等,這些異常的特點是必須被代碼拋出或捕獲,否則編譯都無法通過。

@Transactional 有效場景

  1. 正常執行寫操作

    @Override
    @Transactional
    public int updateById(Employee employee, Integer empId) {
        return employeeMapper.updateById(employee, empId);
    }
    
  2. unchecked 異常

    • RuntimeException

      @Override
      @Transactional
      public int updateByIdRuntimeException(Employee employee, Integer empId) {
          int effect = employeeMapper.updateById(employee, empId);
          System.out.println(1 / 0);
          return effect;
      }
      

      調用此方法,在執行此方法的語句System.out.println(1 / 0);時會拋出運行時異常中的算術異常(ArithmaticException),事務會回滾,數據庫中的數據不會被改變。

    • OutOfMemoryError

      寫一個類來製造堆內存溢出

      public class HeapOOM {
      
          static class OOMObject {
          }
      
          public static void mockHeapOOM() {
              List<Object> objects = new ArrayList<>();
      
              while (true) {
                  objects.add(new OOMObject());
              }
          }
      }
      

      可以調用此類的靜態方法 mockHeapOOM 不停地創建 OOMObject 對象並將其添加至數組中,而被數組強引用的對象無法被 GC 回收,堆內存很快被消耗完畢。

      @Override
      @Transactional
      public int updateByIdOOM(Employee employee, Integer empId) {
          int effect = employeeMapper.updateById(employee, empId);
          HeapOOM.mockHeapOOM();
          return effect;
      }
      

      打開 IntelliJ IDEA 的 Edit Configuration,在 VM Options 一欄爲調用 updateByIdOOM 方法的單元測試配置好 JVM 參數。

      -ea -Xmx10m -Xms5m  -XX:+HeapDumpOnOutOfMemoryError
      
      • -ea 可以打開斷言機制
      • -Xmx10m -Xms5m 表示堆最大可用值是 10M,堆初始值爲 5M
      • -XX:+HeapDumpOnOutOfMemoryError 打印堆溢出報錯信息

      運行單元測試方法,控制檯中報如下錯誤信息:

      java.lang.OutOfMemoryError: GC overhead limit exceeded
      

      查看數據庫,數據沒有被改變,所以此時事務正常回滾。

@Transactional 無效場景

  1. checked 異常

    @Override
    @Transactional
    public void updateByIdException(Employee employee, Integer empId) throws IOException {
        employeeMapper.updateById(employee, empId);
        throw new IOException("模擬 IO 錯誤");
    }
    

    調用此方法,會在throw new IOException("模擬 IO 錯誤")時拋出模擬的錯誤,但是事務並不回滾,數據庫中的數據發生了改變。這是因爲 IOException 屬於 checked 異常,@Transactional 默認情況下不對 checked 異常進行事務回滾。此時,想要在拋出 checked 異常時進行事務回滾,需要在 @Transactional後面配置上rollBackFor屬性:

    @Override
    @Transactional(rollbackFor = IOException.class)
    public void updateByIdException(Employee employee, Integer empId) throws IOException {
        employeeMapper.updateById(employee, empId);
        throw new IOException("模擬 IO 錯誤");
    }
    

    這裏配置爲rollbackFor = Exception.class可以。

  2. 被沒有事務控制的方法調用

    如果帶有 @Transactional的方法 A 被另一個不帶有@Transactional的方法 B 調用,那麼調用 B 時 A 的@Transactional也會失效。

    @Override
    @Transactional
    public int updateByIdRuntimeException(Employee employee, Integer empId) {
        int effect = employeeMapper.updateById(employee, empId);
        System.out.println(1 / 0);
        return effect;
    }
    
    @Override
    public int updateByIdWithoutTransactional(Employee employee, Integer empId) {
        return updateByIdRuntimeException(employee, empId);
    }
    
    

    此時,調用 updateByIdWithoutTransactional,數據庫的數據會被改變,這是由於事務不會因爲在updateByIdRuntimeException中拋出 ArithmaticException而回滾。

發佈了85 篇原創文章 · 獲贊 335 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章