(1)事物的7个传播行为:
org.springframework.transaction.annotation中的枚举类Propagation有以下七个值:
Propagation.REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
Propagation.SUPPORTS-- 支持当前事务,如果当前没有事务,就以非事务方式执行。
Propagation.MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
Propagation.REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
Propagation.NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
Propagation.NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
Propagation.NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
在声明事务@Transactional(propagation=Propagation.REQUIRES)是可以指定事务类型。Propagation.REQUIRES是spring默认事务类型。
(2)当在主ServiceA中执行方法doSomeA(),中间调用ServiceB中doSomeB(),在默认事务(Propagation.REQUIRES)的情况下经过测试结果如下:
//serviceA.doSomeA,插入数据1、4
@Transactional
public void doSomeA() {
myMapper.insertTestDb("1");
serviceB.doSomeB();
//int throwExcept = Integer.valueOf("err");//异常点
myMapper.insertTestDb("4");
}
//serviceB.doSomeB,插入数据2、3
@Transactional
public String doSomeB(){
myMapper.insertTestDb("2");
myMapper.insertTestDb("3");
}
执行顺序 | 方法位置 | db操作 | 值 |
---|---|---|---|
1 | 外层serviceA.doSomeA | insert | 1 |
2 | 内层serviceB.doSomeB | insert | 2 |
3 | 内层serviceB.doSomeB | insert | 3 |
4 | 外层serviceA.doSomeA | insert | 4 |
在不同的地方设置异常操作int throwExcept = Integer.valueOf("err");
执行顺序 | 方法 | |||
1 | serviceA.doSomeA | insert | 1 | |
2 | 异常点1 | |||
3 | serviceB.doSomeB | 异常点2 | ||
4 | insert | 2 | ||
5 | 异常点3 | |||
6 | insert | 3 | ||
7 | 异常点4 | |||
8 | serviceA.doSomeA | 异常点5 | ||
9 | insert | 4 | ||
10 | 异常点6 |
以下是在不同位置出现异常的db插入结果,可以看出在serviceB.doSomeB内部的事务与外部的serviceA.doSomeA操作上是一个原子,可以类似的看作一个事务的执行来判断。
这种内外层都有事务的情况和只有外层事务的效果应该是一致的。
PROPAGATION.REQUIRED | 内外都有事务 | 抛出异常(不进行异常处理) | |||||
正常执行 | 异常点1 | 异常点2 | 异常点3 | 异常点4 | 异常点5 | 异常点6 | |
1 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | |
2 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | |
3 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | |
4 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | |
内外都有事务 | 异常所在方法内捕获(try...catch...) | ||||||
正常执行 | 异常点1 | 异常点2 | 异常点3 | 异常点4 | 异常点5 | 异常点6 | |
1 | 1 | 1 | 1 | 1 | 1 | 1 | |
2 | 未插入 | 未插入 | 2 | 2 | 2 | 2 | |
3 | 未插入 | 未插入 | 未插入 | 3 | 3 | 3 | |
4 | 未插入 | 4 | 4 | 4 | 未插入 | 4 | |
内外都有事务 | 捕获异常后手动回滚(RollbackOnly) | ||||||
正常执行 | 异常点1 | 异常点2 | 异常点3 | 异常点4 | 异常点5 | 异常点6 | |
1 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | |
2 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | |
3 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | |
4 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 | 未插入 |
(3)Propagation.REQUIRES_NEW
当serviceB.doSomeB设置为@Transactional(propagation=Propagation.REQUIRES_NEW)时,外层serviceA.doSomeA的事务在执行到serviceB.doSomeB时会A中事务被挂起,等doSomeB执行完了,结果会立即插入db中,不会等待doSomeA执行结束。因此doSomeA中出现异常也不会导致doSomeB中回滚。
如果doSomeB中抛出异常,在doSomeB内部被捕获了,然后手动进行回滚,影响范围也只局限于doSomeB中,不会导致doSomeA中事务被回滚。
因此,当doSomeA与doSomeB中操作不需要作为一个原子时,可以独立起一个事务,做到互不影响。