記錄一次上線以后出現異常數據庫事務不會滾的情況
情況:接手別人祖傳代碼,代碼的邏輯 就是定時任務 中更新數據庫操作,在更新數據庫操作時候出現了異常,但是數據庫沒有回滾,導致的情況就是數據庫數據不一致了!!!
模擬當時代碼情況,定時任務是60s檢測更新一次,因為事務失效,導致添加了很多重復數據
@Service public class TestTransactionService { @Autowired public TestTransactionMapper testTransactionMapper; @Scheduled(cron = "*/1 * * * * ?") public void callerMethod(){ calledMethod(); } @Transactional(rollbackFor = Exception.class) public void calledMethod(){ //模擬.......很多表的更新添加等業務邏輯 Integer integer = testTransactionMapper.selectCount(new LambdaQueryWrapper<>()); TestTransaction name = TestTransaction.builder() .name(integer + "次更新") .build(); testTransactionMapper.insert(name); System.out.printf("更新成功:"); System.out.printf(""+1/0); } }
解決辦法:
1.最簡單的辦法就是在調用callerMethod() 上面 加入注解@Transactional(rollbackFor = Exception.class)
@Service public class TestTransactionService { @Autowired public TestTransactionMapper testTransactionMapper; @Transactional(rollbackFor = Exception.class) @Scheduled(cron = "*/1 * * * * ?") public void callerMethod(){ calledMethod(); } @Transactional(rollbackFor = Exception.class) public void calledMethod(){ //模擬.......很多表的更新添加等業務邏輯 Integer integer = testTransactionMapper.selectCount(new LambdaQueryWrapper<>()); TestTransaction name = TestTransaction.builder() .name(integer + "次更新") .build(); testTransactionMapper.insert(name); System.out.printf("更新成功:"); System.out.printf(""+1/0); } }
2.在當前類中注入自己代碼如下:
@Service public class TestTransactionService { @Autowired public TestTransactionMapper testTransactionMapper; @Autowired public TestTransactionService testTransactionService; @Scheduled(cron = "*/1 * * * * ?") public void callerMethod(){ testTransactionService.calledMethod(); } @Transactional(rollbackFor = Exception.class) public void calledMethod(){ //模擬.......很多表的更新添加等業務邏輯 Integer integer = testTransactionMapper.selectCount(new LambdaQueryWrapper<>()); TestTransaction name = TestTransaction.builder() .name(integer + "次更新") .build(); testTransactionMapper.insert(name); System.out.printf("更新成功:"); System.out.printf(""+1/0); } }
3.調用代理類的方式
@Service public class TestTransactionService { @Autowired public TestTransactionMapper testTransactionMapper; @Transactional(rollbackFor = Exception.class) @Scheduled(cron = "*/1 * * * * ?") public void callerMethod(){ TestTransactionService t= (TestTransactionService) AopContext.currentProxy(); t.calledMethod(); } @Transactional(rollbackFor = Exception.class) public void calledMethod(){ //模擬.......很多表的更新添加等業務邏輯 Integer integer = testTransactionMapper.selectCount(new LambdaQueryWrapper<>()); TestTransaction name = TestTransaction.builder() .name(integer + "次更新") .build(); testTransactionMapper.insert(name); System.out.printf("更新成功:"); System.out.printf(""+1/0); } }
記得還有一種方法是不需要在啟動類加的
如果啟動程序的時候報錯:Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.
需要在啟動類加上:@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
4.將需要調用的方法,單獨寫到另一個Service中,在通過注入該Service進行調用
記錄這次線上的一次問題
在同一個類中 spring事務是AOP 動態代理實現,一個類中 方法調用是不走代理,只記錄問題 分析原因可以從事務的傳播機制,以及事務的實現方法着手!!!!