diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java index 894580699ec..39ac0bbfea1 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java @@ -416,7 +416,8 @@ public abstract class AbstractPlatformTransactionManager "isolation level will effectively be ignored: " + def); } boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); - return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null); + SuspendedResourcesHolder suspendedResources = suspend(null); + return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, suspendedResources); } } diff --git a/spring-tx/src/test/java/org/springframework/transaction/support/TransactionSupportTests.java b/spring-tx/src/test/java/org/springframework/transaction/support/TransactionSupportTests.java index da661fdd9c7..4a7dd6ebf59 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/support/TransactionSupportTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/support/TransactionSupportTests.java @@ -16,34 +16,23 @@ package org.springframework.transaction.support; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; - -import org.springframework.transaction.IllegalTransactionStateException; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.TransactionSystemException; +import org.springframework.transaction.*; import org.springframework.transaction.testfixture.MockCallbackPreferringTransactionManager; import org.springframework.transaction.testfixture.TestTransactionExecutionListener; import org.springframework.util.ReflectionUtils; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatRuntimeException; -import static org.springframework.transaction.TransactionDefinition.ISOLATION_REPEATABLE_READ; -import static org.springframework.transaction.TransactionDefinition.ISOLATION_SERIALIZABLE; -import static org.springframework.transaction.TransactionDefinition.PROPAGATION_MANDATORY; -import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRED; -import static org.springframework.transaction.TransactionDefinition.PROPAGATION_SUPPORTS; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.transaction.TransactionDefinition.*; +import static org.springframework.transaction.support.AbstractPlatformTransactionManager.SYNCHRONIZATION_ALWAYS; import static org.springframework.transaction.support.AbstractPlatformTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION; import static org.springframework.transaction.support.DefaultTransactionDefinition.PREFIX_ISOLATION; import static org.springframework.transaction.support.DefaultTransactionDefinition.PREFIX_PROPAGATION; @@ -78,6 +67,58 @@ class TransactionSupportTests { .isThrownBy(() -> tm.getTransaction(new DefaultTransactionDefinition(PROPAGATION_MANDATORY))); } + @Test + void noExistingTransactionWithExistingAnotherTransactionManager() { + AbstractPlatformTransactionManager tm1 = new TestTransactionManager(false, true); + tm1.setTransactionSynchronization(SYNCHRONIZATION_ALWAYS); + + DefaultTransactionDefinition txDef1 = + new DefaultTransactionDefinition(PROPAGATION_REQUIRED); + txDef1.setName("tx1"); + txDef1.setReadOnly(false); + txDef1.setIsolationLevel(ISOLATION_READ_COMMITTED); + + DefaultTransactionStatus txStatus1 = (DefaultTransactionStatus)tm1.getTransaction(txDef1); + + // assert for txStatus1 and TransactionSynchronizationManager property + assertThat(txStatus1.hasTransaction()).as("Must have transaction").isTrue(); + assertThat(txStatus1.isNewTransaction()).as("Must be new transaction").isTrue(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()) + .as("Transaction1 Must be readOnly false") + .isEqualTo(txStatus1.isReadOnly()); + assertThat(TransactionSynchronizationManager.getCurrentTransactionName()) + .as("TransactionSynchronizationManager have correct transaction name") + .isEqualTo(txStatus1.getTransactionName()); + assertThat(TransactionSynchronizationManager.getCurrentTransactionIsolationLevel()) + .as("isolation level must be default").isNull(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + + // Setting another trnasaction manager + AbstractPlatformTransactionManager tm2 = new TestTransactionManager(false, true); + tm2.setTransactionSynchronization(SYNCHRONIZATION_ALWAYS); + + // Opening a new transaction before `transaction 1` commits. + DefaultTransactionDefinition txDef2 = + new DefaultTransactionDefinition(PROPAGATION_SUPPORTS); + txDef2.setReadOnly(true); + txDef2.setIsolationLevel(ISOLATION_REPEATABLE_READ); + txDef2.setName("tx2"); + + // assert for txStatus1 and TransactionSynchronizationManager property + DefaultTransactionStatus txStatus2 = (DefaultTransactionStatus) + tm2.getTransaction(txDef2); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).as("Must not have transaction") + .isEqualTo(txStatus2.hasTransaction()); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).as("Must be readOnly true") + .isEqualTo(txStatus2.isReadOnly()); + assertThat(TransactionSynchronizationManager.getCurrentTransactionIsolationLevel()) + .as("isolation level must be Repeatable Read") + .isEqualTo(ISOLATION_REPEATABLE_READ); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + + TransactionSynchronizationManager.clearSynchronization(); + } + @Test void existingTransaction() { PlatformTransactionManager tm = new TestTransactionManager(true, true);