Spring transaction propagation behavior - test code + source code analysis

There are seven types of spring transactions as follows

  • REQUIRED By default, a transaction is required, if not, create a new one
  • REQUIRED_NEW requires a new transaction, new each time
  • SUPPORTS supports transactions but does not enforce transactions
  • NOT_SUPPORTS does not support transactions, a transaction suspends the transaction, and then wakes up after the method is executed
  • MONDATORY forces a transaction, no transaction throws an exception
  • NEVER The current method cannot have a transaction, otherwise an exception will be thrown
  • NESTED nested transaction, whether there is a transaction or not, an internal transaction transaction will be created, the internal rollback will not be rolled back, the external rollback will be rolled back internally

test code

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceTest {

    @Autowired UserService service;

    //////////////////////////////////////////////////////////////////
    //   可以发现在调用方法中有事务的话,被调用方法不会新建事务,直接执行
    //   可以发现在调用方法中没有事务的话,会新建一个事务用于执行
    //////////////////////////////////////////////////////////////////
    @Test
    public void testTransaction1() throws Exception {
        service.testRequiredNoTransaction();
    }

    @Test
    public void testTransaction2() throws Exception {
        service.testRequiredHasTransaction();
    }

    //////////////////////////////////////////////////////////////////
    //   可以发现无论有没有事务,supports都不会去创建一个事务
    //////////////////////////////////////////////////////////////////

    @Test
    public void testTransaction3() throws Exception {
        service.testSupportsOfHasTransaction();
    }


    @Test
    public void testTransaction4() throws Exception {
        service.testSupportsOfNoTransaction();
    }



    //////////////////////////////////////////////////////////////////
    //   可以发现无论有没有事务,required都会创建一个事务
    //////////////////////////////////////////////////////////////////

    @Test
    public void testTransaction5() throws Exception {
        service.testRequiredNewOfHasTransaction();
    }


    @Test
    public void testTransaction6() throws Exception {
        service.testRequiredNewOfNoTransaction();
    }

    //////////////////////////////////////////////////////////////////
    // mandatory在没有事务的环境下会抛出异常IllegalTransactionStateException
    // mandatory在由事务的环境下正常运行
    //////////////////////////////////////////////////////////////////

    @Test
    public void testTransaction7() throws Exception {
        try {
            service.testMandatoryOfNoTransaction();
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    @Test
    public void testTransaction8() throws Exception {
        try {
            service.testMandatoryOfHasTransaction();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //////////////////////////////////////////////////////////////////
    // nested 默认会创建一个nested transaction
    // 在外层事务抛出异常下,内层事务会回滚 test11
    // 在内层事务抛出异常下,外层事务不会回滚 test12
    //
    //////////////////////////////////////////////////////////////////

    @Test
    public void testTransaction9() throws Exception {
        try {
            service.testNestedOfNoTransaction();
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    @Test
    public void testTransaction10() throws Exception {
        try {
            service.testNestedOfHasTransaction();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Test
    public void testTransaction11() throws Exception {
        try {
            service.testNestedOfHasTransactionWithException();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Test
    public void testTransaction12() throws Exception {
        try {
            service.testNestedOfHasTransactionWithNestedException();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //////////////////////////////////////////////////////////////////
    // never在有事务的环境下会抛出异常IllegalTransactionStateException
    // never在没有事务的环境下正常运行,与mandatory正好相反
    //////////////////////////////////////////////////////////////////

    @Test
    public void testTransaction13() throws Exception {
        try {
            service.testNeverOfNoTransaction();
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    @Test
    public void testTransaction14() throws Exception {
        try {
            service.testNeverOfHasTransaction();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //////////////////////////////////////////////////////////////////
    // not_supported 在有事务的环境下会将当前事务挂起,执行完当前方法结束会再把事务唤醒
    // not_supported 在没有事务的环境下直接运行。不等待
    //////////////////////////////////////////////////////////////////

    @Test
    public void testTransaction15() throws Exception {
        try {
            service.testNotSupportedOfHasTransaction();
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    @Test
    public void testTransaction16() throws Exception {
        try {
            service.testNotSupportedOfNoTransaction();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

External transaction (or called transaction caller)


@Service
public class UserService {

    @Autowired
    ShopService shopService;

    private final UserMapper mapper;

    @Autowired
    public UserService(UserMapper mapper) {
        this.mapper = mapper;
    }

    public User findByName(String name) {
        return mapper.selectByPrimaryKey(name);
    }

    @Transactional
    public int updateUser(User user) {
        mapper.updateByPrimaryKeySelective(user);
        Object proxy = AopContext.currentProxy();
        System.out.println(proxy);
        throw new BaseException("-1", "我就不想让你更新成功");
    }

    //////////////////////////////////////////////////////////////////////////////////////////////


    public void testRequiredNoTransaction(){
        //shopService.testA();
        System.out.println(">>>>>>>>>>>>>>>> 我没有事务,我调用一个事务传播行为Propagation.REQUIRED,开始");
        shopService.testTransaction1();
        System.out.println(">>>>>>>>>>>>>>>> 我没有事务,我调用一个事务传播行为Propagation.REQUIRED,结束");
    }

    @Transactional
    public void testRequiredHasTransaction(){
        //shopService.testA();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.REQUIRED,开始");
        shopService.testTransaction1();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.REQUIRED,结束");
    }

    //////////////////////////////////////////////////////////////////////////////////////////////

    @Transactional
    public void testSupportsOfHasTransaction(){
        //shopService.testA();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.SUPPORTS,开始");
        shopService.testTransactionSupports();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.SUPPORTS,结束");
    }


    public void testSupportsOfNoTransaction(){
        //shopService.testA();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.SUPPORTS,开始");
        shopService.testTransactionSupports();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.SUPPORTS,结束");
    }

    //////////////////////////////////////////////////////////////////////////////////////////////

    @Transactional
    public void testRequiredNewOfHasTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.REQUIRED_NEW,开始");
        shopService.testTransactionRequiredNew();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.REQUIRED_NEW,结束");
    }

    public void testRequiredNewOfNoTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.REQUIRED_NEW,开始");
        shopService.testTransactionRequiredNew();
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.REQUIRED_NEW,结束");
    }

    //////////////////////////////////////////////////////////////////////////////////////////////

    @Transactional
    public void testMandatoryOfHasTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.MANDATORY,开始");
        shopService.testTransactionMandatory();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.MANDATORY,结束");
    }

    public void testMandatoryOfNoTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.MANDATORY,开始");
        shopService.testTransactionMandatory();
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.MANDATORY,结束");
    }

    //////////////////////////////////////////////////////////////////////////////////////////////

    @Transactional
    public void testNestedOfHasTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NESTED,开始");
        shopService.testTransactionNested();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NESTED,结束");
    }


    @Transactional
    public void testNestedOfHasTransactionWithException() {
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NESTED,开始");
        shopService.testTransactionNested();
        throw new BaseException("-1", "外层调用函数抛出异常!!");
    }

    @Transactional
    public void testNestedOfHasTransactionWithNestedException() {
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NESTED,开始");
        User user = new User();
        user.setName("wuhulala");
        user.setRemark("内层事务回滚。外层事务不回滚!!!");
        mapper.updateByPrimaryKeySelective(user);
        try {
            shopService.testTransactionNestedWithException();
        } catch (Exception e){
            // do nothing
        }
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NESTED,结束");
    }

    public void testNestedOfNoTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.NESTED,开始");
        shopService.testTransactionNested();
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.NESTED,结束");
    }

    //////////////////////////////////////////////////////////////////////////////////////////////

    @Transactional
    public void testNeverOfHasTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NEVER,开始");
        shopService.testTransactionNever();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NEVER,结束");
    }

    public void testNeverOfNoTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.NEVER,开始");
        shopService.testTransactionNever();
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.NEVER,结束");
    }

    //////////////////////////////////////////////////////////////////////////////////////////////

    @Transactional
    public void testNotSupportedOfHasTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NOT_SUPPORTED,开始");
        shopService.testTransactionNotSupported();
        System.out.println(">>>>>>>>>>>>>>>> 我有事务,我调用一个事务传播行为Propagation.NOT_SUPPORTED,结束");
    }

    public void testNotSupportedOfNoTransaction() {
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.NOT_SUPPORTED,开始");
        shopService.testTransactionNotSupported();
        System.out.println(">>>>>>>>>>>>>>>> 我没事务,我调用一个事务传播行为Propagation.NOT_SUPPORTED,结束");
    }
}

Internal transaction (or called transaction party)

@Service
public class ShopService {


    @Autowired
    private UserMapper userMapper;


    @Transactional(propagation = Propagation.REQUIRED)
    public void testTransaction1() {
        System.out.println("我就是测试事务传播行为PROPAGATION_REQUIRED的");
        System.out.println("表示当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)");
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    public void testTransactionSupports() {
        System.out.println("我就是测试事务传播行为PROPAGATION_SUPPORTS的");
        System.out.println("表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行\n" +
                "\n");
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void testTransactionMandatory() {
        System.out.println("表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常");
        System.out.println("我就是测试事务传播行为PROPAGATION_MANDATORY的");
    }


    @Transactional(propagation = Propagation.NESTED)
    public void testTransactionNested() {
        System.out.println("我就是测试事务传播行为PROPAGATION_NESTED的");
        System.out.println("表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,\n" +
                "被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。\n" +
                "如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。\n" +
                "如果封装事务不存在,则同PROPAGATION_REQUIRED的一样\n" +
                "\n");
    }



    @Transactional(propagation = Propagation.NESTED)
    public void testTransactionNestedWithException() {
        User user = new User();
        user.setName("hlx");
        user.setRemark("hlx-内层事务回滚。外层事务不回滚!!!");
        userMapper.updateByPrimaryKeySelective(user);
        System.out.println("我就是测试事务传播行为PROPAGATION_NESTED抛出异常的");
        System.out.println("表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,\n" +
                "被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。\n" +
                "如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。\n" +
                "如果封装事务不存在,则同PROPAGATION_REQUIRED的一样\n" +
                "\n");
        throw new BaseException("-1", "内层事务抛出异常");
    }


    @Transactional(propagation = Propagation.NEVER)
    public void testTransactionNever() {
        System.out.println("我就是测试事务传播行为PROPAGATION_NEVER的");
        System.out.println("表示当方法不应该在一个事务中运行,如果存在一个事务,则抛出异常\n" +
                "\n");
    }


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void testTransactionRequiredNew() {
        System.out.println("我就是测试事务传播行为PROPAGATION_REQUIRES_NEW的");
        System.out.println("表示当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。\n" +
                "\n");
    }


    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void testTransactionNotSupported() {
        System.out.println("我就是测试事务传播行为PROPAGATION_NOT_SUPPORTED的");
        System.out.println("表示该方法不应该在一个事务中运行。\n" +
                "如果有一个事务正在运行,事务将被挂起,直到当前方法执行完成才恢复执行\n" +
                "\n");
        System.out.println("睡眠5s");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("睡醒了");
    }
}

```java
@Override
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();

        // Cache debug flag to avoid repeated checks.
        boolean debugEnabled = logger.isDebugEnabled();

        if (definition == null) {
            // Use defaults if no transaction definition given.
            definition = new DefaultTransactionDefinition();
        }

        // 如果当前线程中存在事务
        if (isExistingTransaction(transaction)) {
            // Existing transaction found -> check propagation behavior to find out how to behave.
            // 判断是否和当前方法的事务行为有冲突,具体看下面解释
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }

        // Check definition settings for new transaction.
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }

        // No existing transaction found -> check propagation behavior to find out how to proceed.
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);
            if (debugEnabled) {
                logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
            }
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
            catch (RuntimeException ex) {
                resume(null, suspendedResources);
                throw ex;
            }
            catch (Error err) {
                resume(null, suspendedResources);
                throw err;
            }
        }
        else {
            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                        "isolation level will effectively be ignored: " + definition);
            }
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }

    /**
     * Create a TransactionStatus for an existing transaction.
     */
    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'");
        }

        // 如果当前方法的事务行为是NOT_SUPPORTED,挂起当前事务,执行完成之后,再次唤醒
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
            if (debugEnabled) {
                logger.debug("Suspending current transaction");
            }
            Object suspendedResources = suspend(transaction);
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(
                    definition, null, false, newSynchronization, debugEnabled, suspendedResources);
        }

        // 如果当前方法的事务行为是REQUIRED_NEW,挂起当前事务,并新建一个事务执行,执行完之后,唤醒上个事务
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
            if (debugEnabled) {
                logger.debug("Suspending current transaction, creating new transaction with name [" +
                        definition.getName() + "]");
            }
            SuspendedResourcesHolder suspendedResources = suspend(transaction);
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
            catch (RuntimeException beginEx) {
                resumeAfterBeginException(transaction, suspendedResources, beginEx);
                throw beginEx;
            }
            catch (Error beginErr) {
                resumeAfterBeginException(transaction, suspendedResources, beginErr);
                throw beginErr;
            }
        }

        // 如果当前方法的事务行为是NESTED,创建一个保存点。
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            if (!isNestedTransactionAllowed()) {
                throw new NestedTransactionNotSupportedException(
                        "Transaction manager does not allow nested transactions by default - " +
                        "specify 'nestedTransactionAllowed' property with value 'true'");
            }
            if (debugEnabled) {
                logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
            }
            if (useSavepointForNestedTransaction()) {
                // Create savepoint within existing Spring-managed transaction,
                // through the SavepointManager API implemented by TransactionStatus.
                // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
                DefaultTransactionStatus status =
                        prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
                status.createAndHoldSavepoint();
                return status;
            }
            else {
                // Nested transaction through nested begin and commit/rollback calls.
                // Usually only for JTA: Spring synchronization might get activated here
                // in case of a pre-existing JTA transaction.
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, null);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
        }

        // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
        if (debugEnabled) {
            logger.debug("Participating in existing transaction");
        }
        if (isValidateExistingTransaction()) {
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
                Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
                    Constants isoConstants = DefaultTransactionDefinition.constants;
                    throw new IllegalTransactionStateException("Participating transaction with definition [" +
                            definition + "] specifies isolation level which is incompatible with existing transaction: " +
                            (currentIsolationLevel != null ?
                                    isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
                                    "(unknown)"));
                }
            }
            if (!definition.isReadOnly()) {
                if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                    throw new IllegalTransactionStateException("Participating transaction with definition [" +
                            definition + "] is not marked as read-only but existing transaction is");
                }
            }
        }
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
    }

“`

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326407951&siteId=291194637