@Transactional事务生效问题

5次阅读

共计 1667 个字符,预计需要花费 5 分钟才能阅读完成。

平时我们使用 spring 框架,不论是 springmvcv 还是 springboot,springCloud,绝大多数情况我们都是在方法,或者直接在类上面加一个 @Transactional,将事务交给 spring 替我们去管理,然后并没有具体分析一些情况,今天结合几个例子,结合源代码,使用伪代码解释一波。1. 情况一
service(){
// 方法 A
methodA(){
insertA();
}
// 方法 B
@Transactional
methodB(){
insertB();
throw new RunTimeException(“ 强制抛一个异常 ”);
}
public void static main(String[] args){
methodA();
methodB();
}
}
情况一就是这样,main 方法里面顺序调用 AB 两个方法,A 方法不加事务注解,B 方法加了事务注解。如果不了解 @Transactional 事务的传播性,可能会回答:A 成功插入,B 插入失败, 但是实际情况却是 A,B 均插入成功了。到底是什么原因呢?这里先简单介绍一下事务的 6 个传播属性:
PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务,这也是最常见的 PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务的方式执行 PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛异常 PROPAGATION_REQUIRES_NEW:新建事务,如果当前事务存在,就把当前事务挂起 PROPAGATION_NOT_SUPPORTED:以非事务的方式执行,如果存在当前事务,就把当前事务挂起 PROPAGATION_NEVER:以非事务的方式执行,如果当前存在事务,就抛异常 PROPAGATION_NESTED:如果存在当前事务,则在嵌套事务内执行,如果当前没有事务,则新建一个事务
前六个策略类似于 EJB CMT,第七个(PROPAGATION_NESTED)是 Spring 所提供的一个特殊变量。
研究源码,调试程序可以看到:A 没有事务管理,则线程内的 connection 有个 autoCommit = trueB 得到事务的时候,由于事务的传播性依然生效, 得到的还是 A 方法的 commit, 其 autoCommit = true, 故而逐条 sql 进行提交,即 A,B 都会插入
下面我们来分析情况二:
serviceA(){
methodA(){
insertA();
}
}
serviceB(){
@Transactional
methodB(){
insertB();
throw new RuntimeExcption(“ 强制抛出的异常 ”);
}
}
serviceC(){
@Autowired
private ServiceA serviceA;
@Autowired
private ServiceB serviceB;

public void staic main(String[] args){
serviceA.methodA();
serviceB.methodB();
}
}

情况二的主要代码和情况一一样,都是要调用 methodA 和 methodB, 但是结果却不同,情况二的正确结果是指挥插入 A,而 B 会回滚,这是为什么呢?同样是在 B 方法上面加了事务注解 ….
其实大家都知道,spring 的事务是交由 cglib 动态代理的,而动态代理对象产生的时机就非常重要了。再回到本例子:
A:在同一个 service 内部,事务之间嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务 (因为 shpring 使用的是动态代理的方式来控制的事务, 而动态代理最终都是要调用原始对象的,而原始对象在调用方法时,已存在代理对象,是不会再触发代理了!)B:两个方法在不同的 service 里 (即不同的对象,即代理对象也不是同一个), 在 ServiceC 中,使用注入的方式将 serviceA 和 serviceB 注入, 这样即使 A 没有使用事务,B 也有自己的代理,会根据 PROPAGATION_REQUIRED 而生成新的事务.

正文完
 0