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 | 85 ++++++++++++++----- 2 files changed, 65 insertions(+), 23 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 894580699e..39ac0bbfea 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 da661fdd9c..4a7dd6ebf5 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 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.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 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.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 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); 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 | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 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 4a7dd6ebf5..3e604d73b7 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 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.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.*; -import static org.springframework.transaction.TransactionDefinition.*; +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.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_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.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;