@Transactional 特性
@Transactional
註解只能應用到 public 修飾符上,對其他修飾符不起作用,但不報錯。@Transactional
一般加到實現類或實現類方法上,不要加到接口或接口方法上。@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 等,這些異常的特點是必須被代碼拋出或捕獲,否則編譯都無法通過。
- unchecked 異常一般爲錯誤或運行時異常
@Transactional 有效場景
-
正常執行寫操作
@Override @Transactional public int updateById(Employee employee, Integer empId) { return employeeMapper.updateById(employee, empId); }
-
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 無效場景
-
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
可以。 -
被沒有事務控制的方法調用
如果帶有
@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
而回滾。