Try Everything Different In My Life.

「🐞DEBUG」记录一次Spring事务不生效的情况

2020.04.15

使用@Transactional注解就可以很简单的开启一个事务,但是在一次开发中发现事务没有起作用

在一次开发中,发现一个事务没有起效果,导致数据库中产生了脏数据,最后通过Google的方法解决了。现在记录一下当时是怎么想的

现象

有一个业务是将excel导进来的数据处理一下再导到数据库中,业务逻辑如下图

代码如下

/**
 *
 *  代码知识模拟了一下
 *  其中testTransaction想当于插入数据方法
 *  testTransactionNoWork相当于转换数据方法
 */
public class InputDataService{

    @Resource
    private LogMapper logMapper;

	@Transactional(rollbackFor = Exception.class)
	public void testTransaction(){
		for (int i = 1; i < 100; i++) {
			Log log = new Log(1,"测试log" + i);					
			logMapper.insert(log);
			System.out.println(i);
			if (i == 3){
				throw new RuntimeException("ERROR");
			}
		}
	}
	
	public void testTransactionNoWork(){
		this.testTransaction();
	}
}

原因

spring事务是使用的AOP的动态代理机制来生成一个代理类来完成的,事务相关操作都是由代理类来完成的。

public void testTransactionNoWork(){
    this.testTransaction();
}

这个方法使用this来调用方式,本质上是实例调用,并没有通过事务代理类来调用方法,所以导致了事务失效。

// TODO

  • 1.Spring中事务实现的原理是什么
  • 2.Spring中AOP实现的原理是什么

解决办法

1.引入自身bean

    @Autowired
    private InputDataService inputDataService;

    public void testTransactionNoWork(){
        this.inputDataService.testTransaction();
    }

2.通过AopContext获取到当前类的代理类

InputDataService inputDataService = (InputDataService)AopContext.currentProxy();
inputDataService.testTransaction();

总结

使用Spring声明式事务不生效的原因有以下两点:

  • API使用不规范
    • 捕捉的异常和抛出的异常不对应
  • 动态代理没有生效
    • 方法自调用(没有走代理类)
    • 方法是不是public修饰的(无法生成代理)

参考

🔗内部方法调用,事务不起作用的原因及解决办法