spring事务传播机制

spring 在TransactionDefinition.class中定义了事务的传播属性。并定义了枚举类Propagation.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package org.springframework.transaction.annotation;

public enum Propagation {
/**
* 如果当前已经存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
/**
* 如果当前已经存在事务,那么加入该事务,否则就以非事务方式执行。
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
/**
* 使用当前的事务,如果当前没有事务,就抛出异常。
*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
/**
* 新建事务,如果当前存在事务,把当前事务挂起。
* 如果当前存在事务,先把当前事务相关内容封装到一个实体,然后重新创建一个新事务,接受这个实体为参数,用于事务的恢复。更直白的说法就是暂停当前事务(当前无事务则不需要),创建一个新事务。 针对这种情况,两个事务没有依赖关系,可以实现新事务回滚了,但外部事务继续执行。
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
/**
* 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
/**
* 以非事务方式执行,如果当前存在事务,则抛出异常。
*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
/**
* 如果当前存在事务,则在嵌套事务内执行。
* 使用 SavePoint 技术把当前事务状态进行保存,然后底层共用一个连接,当NESTED内部出错的时候,自行回滚到 SavePoint这个状态,只要外部捕获到了异常,就可以继续进行外部的事务提交,而不会受到内嵌业务的干扰,但是,如果外部事务抛出了异常,整个大事务都会回滚。
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
private final int value;
Propagation(int value) { this.value = value; }
public int value() { return this.value; }
}

spring事务传播示例

  1. PROPAGATION_REQUIRED

如果当前已经存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。

1
2
3
4
5
6
7
8
9
10
11
@Transactional
public void service() {
serviceA();
serviceB();
}

@Transactional
public void serviceA() {}

@Transactional
public void serviceB() {}

service、serviceA、serviceB都声明了事务,没有指定传播机制,默认为PROPAGATION_REQUIRED,三个方法都在一个事务内,
当三个方法中任意一个出现异常RuntimeException.class,整个事务都会回滚。

  1. PROPAGATION_SUPPORTS

如果当前已经存在事务,那么加入该事务,否则就以非事务方式执行。

1
2
3
4
5
6
7
public void service() {
serviceA();
1/0;
}

@Transactional(propagation = Propagation.SUPPORTS)
public void serviceA() {}

service没有开启事务,serviceA在无事务下执行完成。此时serviceA不会回滚。

1
2
3
4
5
6
7
8
9
10
11
@Transactional
public void service() {
serviceA();
}

@Transactional(propagation = Propagation.SUPPORTS)
public void serviceA() {
exe sql1;
int i = 1/0;
exe sql2;
}

service没有开启事务,serviceA在无事务下执行完成。
我测试的时候,sql1不会回滚。因为使用了druid数据源,默认defaultAutoCommit = true;

  1. PROPAGATION_MANDATORY

使用当前的事务,如果当前没有事务,就抛出异常。

1
2
3
4
5
6
7
8
9
public void service() {
serviceA();
serviceB();
}

public void serviceA() {}

@Transactional(propagation = Propagation.MANDATORY)
public void serviceB() {}

serviceA执行成功,serviceB将会抛出异常

  1. PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Transactional
public void service(){
serviceA();
try {
serviceB();
} catch (Exception e) {
//
}
}
serviceA() {}

@Transactional(propagation=Propagation.REQUIRES_NEW)
serviceB() {
do sql 1
1/0;
do sql 2
}

当调用service接口时,由于serviceB使用的是REQUIRES_NEW,它会创建一个新的事务,
但由于serviceB抛出了运行时异常,导致serviceB整个被回滚了,而在service方法中,捕获了异常,所以serviceA是正常提交的。
注意,service中的try-catch代码是必须的,否则service也会抛出异常,导致serviceA也被回滚。

  1. PROPAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Transactional
public void service() {
serviceA();
serviceB();
}
serviceA(){
exe sql
}
@Transactional(propagation=Propagation.NOT_SUPPORTED)
serviceB() {
exe sql 1
int i = 1/0;
exe sql 2
}

当调用service方法的时候,执行到serviceB方法中的1/0代码时,抛出了异常,
由于serviceB处于无事务环境下,所以 sql1是否生效取决于defaultAutoCommit的值,
当defaultAutoCommit=true时,sql1是生效的。但是service由于抛出了异常,所以serviceA会被回滚。

  1. PROPAGATION_NEVER

以非事务方式执行,如果当前存在事务,则抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void service() {
serviceA();
serviceB();
}
serviceA(){
exe sql
}
@Transactional(propagation=Propagation.NEVER)
serviceB() {
exe sql 1
1/0;
exe sql 2
}

serviceA会执行成功,若defaultAutoCommit=true,则serviceB中的sql1会生效。

  1. PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Transactional
public void service() {
serviceA();
try {
serviceB();
} catch (Exception e) {
//
}
}
serviceA() {
exe sql
}
@Transactional(propagation=Propagation.NESTED)
serviceB() {
exe sql1
int i = 1/0;
exe sql2
}

serviceB是一个内嵌的业务,内部抛出了运行时异常,所以serviceB整个被回滚了,由于service捕获了异常,所以serviceA是可以正常提交的。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Transactional
public void service() {
serviceA();
serviceB();
int i = 1/0;
}
@Transactional(propagation=Propagation.NESTED)
serviceA() {
exe sql
}
serviceB() {
exe sql
}

由于service抛出了异常,所以会导致整个service方法被回滚。(这就是跟PROPAGATION_REQUIRES_NEW不一样的地方了,NESTED方式下的内嵌业务会受到外部事务的异常而回滚。)

参考
https://blog.csdn.net/yanyan19880509/article/details/53041564


spring事务传播机制
https://www.wekri.com/spring/spring-propagation/
Author
Echo
Posted on
June 22, 2018
Licensed under