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