在面试中,经常会遇到这个问题,面试官经常说编程式事务比声明式事务的掌控粒度更细,能够精确到代码行,而声明式事务作为注解使用只能控制整个方法。我个人认为是不太正确的。下面来看一下我的想法
1.编程式事务
public void test() {
int a = 1;
try {
// 开启事务
int b = 10 / 0;
} catch (Exception e) {
System.out.println("出错了");
// 事务回滚
} finally {
// 事务提交
}
}
大概是这样使用,可以看到a变量并不受事务的影响,所以可以实现对方法中的局部代码行进行事务的控制。
2.声明式事务
声明式事务主要用注解来实现。
@Transactional(rollbackFor = {Exception.class}, propagation = Propagation.REQUIRED)
public void insertUser(@RequestBody TbUser user) throws FileNotFoundException {
int a = 1;
int b = 2;
}
大概是这样实现,表面上看起来确实是只能对整个方法进行事务管理。
注:rollbackFor默认回滚RuntimeException的所有子类,即使你想捕捉数组长度异常类再回滚,但实际上抛的ArithmeticException异常,也是会回滚的。具体可以看这位老哥的文章:@Transactional注解的rollbackFor属性 - 简书
3.反转
其实声明式事务依然可以实现对于代码行的控制。
@Transactional(rollbackFor = {Exception.class}, propagation = Propagation.REQUIRED)
public void insertUser(@RequestBody TbUser user) throws FileNotFoundException {
int a = 1;
int b = afterA();
}
@Transactional(rollbackFor = {Exception.class}, propagation = Propagation.REQUIRED)
public int afterA() {
return 2;
}
可以把要实现精确控制的代码写入其他方法,通过调用其他方法来实现事务的精确控制。这里会用到事务的传播行为。但这样的写法也是有问题的
4.存在的问题
第3步的代码是有很大的问题的,你会发现事务的传播行为失效了。并没有按照你想的步骤来执行。是因为如果你想通过这种方式来实现精确控制,是不能把被调用的方法和需要调用的方法写在同一个对象里,具体的原因可以看这位老哥写的,非常透彻:spring 事务Propagation.REQUIRES_NEW 不起作用的原因_hepei120的博客-CSDN博客
最后的代码实现: