Spring的事务传播机制
宋标 Lv5

Spring的事务传播机制

1. REQUIRED

Support a current transaction, create a new one if none exists.

存在事务则加入当前事务,不存在则创建事务。spring事务默认传播级别

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
class Test {

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
jdbcTemplate.execute("insert into user values(null, 'songbiao', 20)");
}

@Test
public void test() {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
TransactionStudy tx = app.getBean(TransactionStudy.class);

DataSourceTransactionManager transactionManager = app.getBean(DataSourceTransactionManager.class);
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
// 开启PROPAGATION_REQUIRED传播行为的事务
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

tx.methodA(); // tx1
tx.methodA(); // tx2

if(true) throw new RuntimeException();

transactionManager.commit(status);
}
}

测试结果: tx1和tx2加入存在的事务,tx1和tx2执行的sql语句会被一起回滚, catch异常也同样会回滚事务。

2. SUPPORTS

Support a current transaction, execute non-transactionally if none exists.

存在事务则加入事务,不存在事务则非事务执行

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
class Test {
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
jdbcTemplate.execute("insert into user values(null, 'xiaowang', 21)");
if (true) {
throw new RuntimeException("SUPPORTS 回滚测试");
}
}

@Test
public void test() {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
TransactionStudy tx = app.getBean(TransactionStudy.class);

DataSourceTransactionManager transactionManager = app.getBean(DataSourceTransactionManager.class);
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();

defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 开启PROPAGATION_REQUIRED传播行为的事务
TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

tx.methodB(); // tx1

transactionManager.commit(status);
}
}

测试结果: tx1加入存在的事务,tx1事务回滚,若不存在事务,tx1以非事务执行,tx1将成功添加一条数据。

3. MANDATORY

Support a current transaction, throw an exception if none exists.

存在事务则加入当前事务,不存在事务则抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Test {

@Transactional(propagation = Propagation.MANDATORY)
public void methodA() {
jdbcTemplate.execute("insert into user values(null, 'songbiao', 20)");
}

@Test
public void test() {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
TransactionStudy tx = app.getBean(TransactionStudy.class);

DataSourceTransactionManager transactionManager = app.getBean(DataSourceTransactionManager.class);
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();

defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 开启PROPAGATION_REQUIRED传播行为的事务
// TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

tx.methodA(); // tx1

// transactionManager.commit(status);
}
}

测试结果:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

4. REQUIRES_NEW

Create a new transaction, and suspend the current transaction if one exists.

创建一个事务,如果存在事务则挂起存在的事务

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
class Test {

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodA() {
jdbcTemplate.execute("insert into user values(null, 'songbiao', 20)");
}

public void methodC() {
jdbcTemplate.execute("insert into user values(null, 'methodC', 22)");
}

@Test
public void test() {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
TransactionStudy tx = app.getBean(TransactionStudy.class);

DataSourceTransactionManager transactionManager = app.getBean(DataSourceTransactionManager.class);
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

// 开启PROPAGATION_REQUIRED传播行为的事务
TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

tx.methodC(); // tx1
tx.methodA(); // tx2

if (true) throw new RuntimeException();

transactionManager.commit(status);
}
}

测试结果: tx1事务回滚,tx2创建了新事务,tx2事务成功添加一条数据

5. NOT_SUPPORTED

Execute non-transactionally, suspend the current transaction if one exists.

非事务执行,如果存在事务则挂起存在的事务。

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
class Test {

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodA() {
jdbcTemplate.execute("insert into user values(null, 'songbiao', 20)");
}

public void methodC() {
jdbcTemplate.execute("insert into user values(null, 'methodC', 22)");
}

@Test
public void test() {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
TransactionStudy tx = app.getBean(TransactionStudy.class);

DataSourceTransactionManager transactionManager = app.getBean(DataSourceTransactionManager.class);
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 开启PROPAGATION_REQUIRED传播行为的事务
TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

tx.methodC(); // tx1
tx.methodA(); // tx2

if (true) throw new RuntimeException();

transactionManager.commit(status);
}
}

测试结果: 由于tx2是非事务执行,tx1事务回滚,tx2事务添加一条数据

6. NEVER

Execute non-transactionally, throw an exception if a transaction exists.

非事务执行,如果存在事务则抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Test {

@Transactional(propagation = Propagation.NEVER)
public void methodA() {
jdbcTemplate.execute("insert into user values(null, 'songbiao', 20)");
}

@Test
public void test() {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
TransactionStudy tx = app.getBean(TransactionStudy.class);

DataSourceTransactionManager transactionManager = app.getBean(DataSourceTransactionManager.class);
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

tx.methodA(); // tx1

transactionManager.commit(status);
}
}

测试结果: org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

7. NESTED

Execute within a nested transaction if a current transaction exists, behave like REQUIRED otherwise.

如果存在事务则嵌套执行事务,若不存在事务退化成REQUIRED传播行为

  1. 案例1
    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
    class Test {

    @Transactional(propagation = Propagation.NESTED)
    public void methodA() {
    jdbcTemplate.execute("insert into user values(null, 'songbiao', 20)");
    throw new RuntimeException("nested exception.");
    }

    public void methodC() {
    jdbcTemplate.execute("insert into user values(null, 'methodC', 22)");
    }

    @Test
    public void test() {
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
    TransactionStudy tx = app.getBean(TransactionStudy.class);

    DataSourceTransactionManager transactionManager = app.getBean(DataSourceTransactionManager.class);
    DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
    defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

    try {
    tx.methodC(); // tx1
    tx.methodA(); // tx2
    } catch (Exception e) {
    e.printStackTrace();
    }

    transactionManager.commit(status);
    }
    }
    若存在事务,保存当前事务状态点,加入存在的事务,执行完嵌套事务后恢复到保存的事务状态点完成事务执行。

测试结果:保存tx1事务状态点,tx2加入存在的事务,tx2抛出异常,回滚tx2的事务状态,恢复到tx1事务状态点,提交事务,tx1事务成功添加一条数据。
2. 案例2

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
class Test {

public A a;

@Transactional(propagation = Propagation.NESTED)
public void methodA() {
jdbcTemplate.execute("insert into user values(null, 'songbiao', 20)");
a.nestTxTest();
}


@Test
public void test() {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
TransactionStudy tx = app.getBean(TransactionStudy.class);

DataSourceTransactionManager transactionManager = app.getBean(DataSourceTransactionManager.class);
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

// TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

tx.methodA(); // tx1

// transactionManager.commit(status);
}
}

public class A {

@Transactional(propagation = Propagation.NEVER)
public void nestTxTest() {
jdbcTemplate.execute("insert into user values(null, 'A', 20)");
}

}

当前不存在事务,NESTED传播行为退化成REQUIRED传播行为。

测试结果: org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never',tx1事务回滚

总结

花了很多的时间来学习测试事务的传播机制,根据实际测试,跟之前的认知还是有一点偏差。
关于使用的场景,默认的传播行为一般用的最多,以后涉及到事务的传播行为需再三斟酌!

 评论