From fe2b40c663512a87a2cf496be560a806782b2a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=83=9C=ED=98=81?= Date: Thu, 12 Jun 2025 22:26:46 +0900 Subject: [PATCH 1/2] add suspend when new transaction with propagation supports, never, not supports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 정태혁 --- .../AbstractPlatformTransactionManager.java | 3 +- .../support/TransactionSupportTests.java | 83 ++++++++++++++----- 2 files changed, 64 insertions(+), 22 deletions(-) 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 894580699ec4..39ac0bbfea13 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 @@ else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUI "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 da661fdd9c70..4a7dd6ebf59b 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 @@ void noExistingTransaction() { .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); From 6279b6a0d18f7c35e0e33454498b21aa5f1407a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=83=9C=ED=98=81?= Date: Thu, 12 Jun 2025 23:20:50 +0900 Subject: [PATCH 2/2] revert optimiz imports in TransactionSupportTests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 정태혁 --- .../support/TransactionSupportTests.java | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) 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 4a7dd6ebf59b..3e604d73b7d8 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,22 +16,36 @@ 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.*; + +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.testfixture.MockCallbackPreferringTransactionManager; import org.springframework.transaction.testfixture.TestTransactionExecutionListener; import org.springframework.util.ReflectionUtils; -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.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_READ_COMMITTED; +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 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;