背景:
@Transaction注解是我们在日常的写代码过程中最常使用的事务注解了,本文就从spring源码的角度解析下这个注解的执行过程,以便分析为什么使用事务比正常的单sql执行更容易导致连接池耗尽
源码追踪:
本文假定使用PROPAGATION_REQUIRED (默认)的事务传播,@Transaction的执行过程的伪代码如下所示:
1.从mysql连接池中获取一条db连接,自此该连接被绑定到当前事务中.
2.执行connection.setAutoCommit(false),即关闭自动事务提交功能
3.执行@Transaction注解的方法,如果执行中途有回滚类的异常,那么走第4步的逻辑,否认走第5步的逻辑
4.执行connection.rollback异常回滚sql操作
5.执行正常的connection.commit事务提交
6.恢复数据库连接的connection.setAutoCommit(true)的自动事务提交功能
7.把connection数据库连接归还给连接池中
TransactionAspectSupport类几乎实现了以上的所有伪代码
以上就是@Transaction注解的大概代码执行流程
问答:
1.为什么大事务会引起db连接池耗尽
答:从源码中明显可以看出,再执行事务之前获取到的数据库连接会一直持有直到事务结束,这期间该连接是不会释放的,所以大事务容易引起DB连接池耗尽
2.@Transaction异常回滚后,外层调用@Transaction的方法还能获取到异常对象吗?
看图,显然,sql回滚后还是会把异常向外抛出.
3.同一类中方法调用@Transaction注解的事务方法无效?
是的,而且不管这个类是JDK动态代码实现的AOP还是CGLIB动态修改类实现的AOP,同一个类内方法调用另一个事务方法就是无效
@Transactional(rollbackFor = {Exception.class})
public boolean insertException() {
boolean saveSuccess = dao.insert(record) > 0;
System.out.println(1/0);
return saveSuccess;
}
public boolean insertCaller(Integer ruleId, Integer ruleType) {
return insertException(ruleId, ruleType);
}
比如,如上代码所示,直接调用insertException方法,是可以异常回滚的,但是如果调用insertCaller方法,事务是无效的,记录会被正常插入.