@Transactional是一種基於注解管理事務的方式,spring通過動態代理的方式為目標方法實現事務管理的增強。
@Transactional使用起來方便,但也需要注意引起@Transactional失效的場景,本文總結了七種情況,下面進行逐一分析。
1、異常被捕獲后沒有拋出
當異常被捕獲后,並且沒有再拋出,那么deleteUserA是不會回滾的。
@Transactional public void deleteUser() { userMapper.deleteUserA(); try { int i = 1 / 0; userMapper.deleteUserB(); } catch (Exception e) { e.printStackTrace(); } }
2、拋出非運行時異常
異步雖然拋出了,但是拋出的是非RuntimeException類型的異常,依舊不會生效。
@Transactional public void deleteUser() throws MyException{ userMapper.deleteUserA(); try { int i = 1 / 0; userMapper.deleteUserB(); } catch (Exception e) { throw new MyException(); } }
如果指定了回滾異常類型為Exception,那么就可以回滾非RuntimeException類型異常了。
@Transactional(rollbackFor = Exception.class)
3、方法內部直接調用
如果先調用deleteUser(),那么deleteUserA()是不會回滾的,其原因就是@Transactional根本沒生成代理,如果直接調用deleteUser2()那么沒問題,deleteUserA()會回滾。
public void deleteUser() throws MyException{ deleteUser2(); } @Transactional public void deleteUser2() throws MyException{ userMapper.deleteUserA(); int i = 1 / 0; userMapper.deleteUserB(); }
修改方式,把當前類自己注入一下調用即可。
@Service public class UserService { @Autowired private UserMapper userMapper; //自己注入自己 @Autowired UserService userService; public void deleteUser() throws MyException{ userService.deleteUser2(); } @Transactional public void deleteUser2() throws MyException{ userMapper.deleteUserA(); int i = 1 / 0; userMapper.deleteUserB(); } }
4、新開啟一個線程
如下的方式deleteUserA()也不會回滾,因為spring實現事務的原理是通過ThreadLocal把數據庫連接綁定到當前線程中,新開啟一個線程獲取到的連接就不是同一個了。
@Transactional public void deleteUser() throws MyException{ userMapper.deleteUserA(); try { //休眠1秒,保證deleteUserA先執行 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { int i = 1/0; userMapper.deleteUserB(); }).start(); }
5、注解到private方法上
idea直接會給出提示Methods annotated with ‘@Transactional’ must be overridable ,原理很簡單,private修飾的方式,spring無法生成動態代理。
@Transactional private void deleteUser() throws MyException{ userMapper.deleteUserA(); int i = 1/0; userMapper.deleteUserB(); }
6、數據庫本身不支持
mysql數據庫,必須設置數據庫引擎為InnoDB。
7、事務傳播屬性設置錯誤
注意傳播屬性的設置,比如設置了:PROPAGATION_NOT_SUPPORIED(以非事務的方式執行,如果當前有事務則把當前事務掛起)。
8、操作數據庫使用了truncate關鍵字
整個業務里面只要有 truncate 就會導致事務回滾失效