关于java同一个类互相调用,spring事务失效问题
记录一次上线以后出现异常数据库事务不会滚的情况
情况:接手别人祖传代码,代码的逻辑 就是定时任务 中更新数据库操作,在更新数据库操作时候出现了异常,但是数据库没有回滚,导致的情况就是数据库数据不一致了!!!
模拟当时代码情况,定时任务是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 动态代理实现,一个类中 方法调用是不走代理,只记录问题 分析原因可以从事务的传播机制,以及事务的实现方法着手!!!!