Spring Tx 传播类型

只要涉及到数据库操作,必定就会使用 @Transactional 注解,其中有一个属性就是 propagation(传播类型),掌握它的用法很重要。演示代码见末尾。

演示事务传播

基础代码

定义了所有的传播类型,第二个参数来控制是否抛出异常

 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
@Transactional(propagation = Propagation.REQUIRED)
public void REQUIRED(User user, boolean throwException) {
    insertUser(user, throwException);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void REQUIRES_NEW(User user, boolean throwException) {
    insertUser(user, throwException);
}

@Transactional(propagation = Propagation.NESTED)
public void NESTED(User user, boolean throwException) {
    insertUser(user, throwException);
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void NOT_SUPPORTED(User user, boolean throwException) {
    insertUser(user, throwException);
}

@Transactional(propagation = Propagation.SUPPORTS)
public void SUPPORTS(User user, boolean throwException) {
    insertUser(user, throwException);
}

@Transactional(propagation = Propagation.NEVER)
public void NEVER(User user, boolean throwException) {
    insertUser(user, throwException);
}

@Transactional(propagation = Propagation.MANDATORY)
public void MANDATORY(User user, boolean throwException) {
    insertUser(user, throwException);
}

REQUIRED_REQUIRED

1
2
3
4
5
6
7
8
@Transactional(propagation = Propagation.REQUIRED)
public void REQUIRED_REQUIRED() {
    serviceA.REQUIRED(new User("111"), false);
    try {
        serviceA.REQUIRED(new User("222"), true);
    } catch (Exception ignored) {
    }
}

结论:不会插入数据, 会抛出异常

分析:第一次调用创建新的事务状态,第二次调用因为是 REQUIRED, 所以会共用之前的事务状态,这样两次调用是同一个事务状态。 第二次调用发生异常,事务状态要回滚,而第一次调用没有异常,事务状态要提交,导致事务状态冲突

REQUIRED_REQUIRES_NEW

1
2
3
4
5
6
7
8
@Transactional(propagation = Propagation.REQUIRED)
public void REQUIRED_REQUIRES_NEW() {
    serviceA.REQUIRED(new User("111"), false);
    try {
        serviceA.REQUIRES_NEW(new User("222"), true);
    } catch (Exception ignored) {
    }
}

结论:会插入 111 数据

分析:第一次调用创建新的事务状态,第二次调用因为是 REQUIRES_NEW, 所以会创建新的事务状态,这样两次调用不是同一个事务状态。 第二次调用发生异常,事务状态要回滚,而第一次调用没有异常,事务状态要提交

REQUIRED_NESTED

1
2
3
4
5
6
7
8
@Transactional(propagation = Propagation.REQUIRED)
public void REQUIRED_NESTED() {
    serviceA.REQUIRED(new User("111"), false);
    try {
        serviceA.NESTED(new User("222"), true);
    } catch (Exception ignored) {
    }
}

结论:会插入 111 数据

分析:第一次调用创建新的事务状态,第二次调用因为是 NESTED, 所以会设置保存点,这样两次调用是同一个事务状态。 第二次调用发生异常,事务状态要回滚保存点,而第一次调用没有异常,事务状态要提交

REQUIRED_NOT_SUPPORTED

1
2
3
4
5
6
7
8
@Transactional(propagation = Propagation.REQUIRED)
public void REQUIRED_NOT_SUPPORTED() {
    serviceA.REQUIRED(new User("111"), false);
    try {
        serviceA.NOT_SUPPORTED(new User("222"), true);
    } catch (Exception ignored) {
    }
}

结论:会插入 111 数据, 222 数据

分析:第一次调用创建新的事务状态,第二次调用因为是 NOT_SUPPORTED, 所以会挂起事务,这样只有第一次调用是有事务。 第二次调用发生异常,因为没有事务,所以不会回滚,而第一次调用没有异常,事务状态要提交

REQUIRED_SUPPORTS

1
2
3
4
5
6
7
8
@Transactional(propagation = Propagation.REQUIRED)
public void REQUIRED_SUPPORTS() {
    serviceA.REQUIRED(new User("111"), false);
    try {
        serviceA.SUPPORTS(new User("222"), true);
    } catch (Exception ignored) {
    }
}

结论:不会插入数据, 会抛出异常

分析:第一次调用创建新的事务状态,第二次调用因为是 SUPPORTS, 所以会共用之前的事务状态,这样两次调用是同一个事务状态。 第二次调用发生异常,事务状态要回滚,而第一次调用没有异常,事务状态要提交,导致事务状态冲突

REQUIRED_NEVER

1
2
3
4
5
6
7
8
@Transactional(propagation = Propagation.REQUIRED)
public void REQUIRED_NEVER() {
    serviceA.REQUIRED(new User("111"), false);
    try {
        serviceA.NEVER(new User("222"), true);
    } catch (Exception ignored) {
    }
}

结论:会插入 111 数据

分析:第一次调用创建新的事务状态,第二次调用因为是 NEVER, 所以会抛出异常不会继续执行代码。 第一次调用没有异常,事务状态要提交

REQUIRED_MANDATORY

1
2
3
4
5
6
7
8
@Transactional(propagation = Propagation.REQUIRED)
public void REQUIRED_MANDATORY() {
    serviceA.REQUIRED(new User("111"), false);
    try {
        serviceA.MANDATORY(new User("222"), true);
    } catch (Exception ignored) {
    }
}

结论:不会插入数据, 会抛出异常

分析:第一次调用创建新的事务状态,第二次调用因为是 REQUIRED, 所以会共用之前的事务状态,这样两次调用是同一个事务状态。 第二次调用发生异常,事务状态要回滚,而第一次调用没有异常,事务状态要提交,导致事务状态冲突

事务传播原理

源码位置: org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction

 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
// 每一个 @Transactional 都会执行下面的方法,来获取事务状态
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {
    ...
    // 获取当前事务
    Object transaction = doGetTransaction();
    // 判断事务是否存在
    if (isExistingTransaction(transaction)) {
        // 重点解析
        return handleExistingTransaction(def, transaction, debugEnabled);
    }
    // 下面是不存在事务的情况
    if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException(
                "No existing transaction found for transaction marked with propagation 'mandatory'");
    }
    else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
            def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        ...
        try {
            // 开启新的事务
            return startTransaction(def, transaction, debugEnabled, suspendedResources);
        }
        catch (RuntimeException | Error ex) {
            resume(null, suspendedResources);
            throw ex;
        }
    }
    else {
        ...
        // 不开始事务
        return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
    }
}

源码位置: org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction

 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private TransactionStatus handleExistingTransaction(
        TransactionDefinition definition, Object transaction, boolean debugEnabled)
        throws TransactionException {
    // 下面是存在事务的情况
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
        throw new IllegalTransactionStateException(
                "Existing transaction found for transaction marked with propagation 'never'");
    }

    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
        ...
        // 挂起当前事务,以非事务来执行
        Object suspendedResources = suspend(transaction);
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(
                definition, null, false, newSynchronization, debugEnabled, suspendedResources);
    }

    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
        ...
        // 挂起当前事务
        SuspendedResourcesHolder suspendedResources = suspend(transaction);
        try {
            // 开启新事务
            return startTransaction(definition, transaction, debugEnabled, suspendedResources);
        }
        catch (RuntimeException | Error beginEx) {
            resumeAfterBeginException(transaction, suspendedResources, beginEx);
            throw beginEx;
        }
    }

    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        ...
        if (useSavepointForNestedTransaction()) {
            ...
            DefaultTransactionStatus status =
                    prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
            // 在当前事务上,创建保存点
            status.createAndHoldSavepoint();
            return status;
        }
        else {
            // 不支持保存点,就开启新事务
            return startTransaction(definition, transaction, debugEnabled, null);
        }
    }

    ...
    // 继续使用当前事务
    return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}

说明:在 startTransaction 方法中,每次都会获取新连接开启事务

代码

demo-spring-transaction-propagation

0%