Introduce TransactionExecutionListener with begin/commit/rollback notifications
Includes ConfigurableTransactionManager interface for listener registration. Includes additional introspection methods on TransactionExecution interface. Includes default method declarations on TransactionStatus/SmartTransactionObject. Closes gh-27479
This commit is contained in:
@@ -72,10 +72,17 @@ public class JtaTransactionManagerTests {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
// something transactional
|
||||
assertThat(status.getTransactionName()).isEqualTo("txName");
|
||||
assertThat(status.hasTransaction()).isTrue();
|
||||
assertThat(status.isNewTransaction()).isTrue();
|
||||
assertThat(status.isNested()).isFalse();
|
||||
assertThat(status.isReadOnly()).isFalse();
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
|
||||
TransactionSynchronizationManager.registerSynchronization(synch);
|
||||
assertThat(TransactionSynchronizationManager.getCurrentTransactionName()).isEqualTo("txName");
|
||||
assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
|
||||
assertThat(status.isRollbackOnly()).isFalse();
|
||||
assertThat(status.isCompleted()).isFalse();
|
||||
}
|
||||
});
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
|
||||
@@ -105,8 +112,15 @@ public class JtaTransactionManagerTests {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
// something transactional
|
||||
assertThat(status.getTransactionName()).isEmpty();
|
||||
assertThat(status.hasTransaction()).isTrue();
|
||||
assertThat(status.isNewTransaction()).isTrue();
|
||||
assertThat(status.isNested()).isFalse();
|
||||
assertThat(status.isReadOnly()).isFalse();
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
|
||||
TransactionSynchronizationManager.registerSynchronization(synch);
|
||||
assertThat(status.isRollbackOnly()).isFalse();
|
||||
assertThat(status.isCompleted()).isFalse();
|
||||
}
|
||||
});
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
|
||||
@@ -134,7 +148,14 @@ public class JtaTransactionManagerTests {
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
assertThat(status.getTransactionName()).isEmpty();
|
||||
assertThat(status.hasTransaction()).isTrue();
|
||||
assertThat(status.isNewTransaction()).isTrue();
|
||||
assertThat(status.isNested()).isFalse();
|
||||
assertThat(status.isReadOnly()).isFalse();
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
|
||||
assertThat(status.isRollbackOnly()).isFalse();
|
||||
assertThat(status.isCompleted()).isFalse();
|
||||
}
|
||||
});
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
|
||||
@@ -160,11 +181,18 @@ public class JtaTransactionManagerTests {
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
assertThat(status.getTransactionName()).isEqualTo("txName");
|
||||
assertThat(status.hasTransaction()).isTrue();
|
||||
assertThat(status.isNewTransaction()).isTrue();
|
||||
assertThat(status.isNested()).isFalse();
|
||||
assertThat(status.isReadOnly()).isFalse();
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
|
||||
TransactionSynchronizationManager.registerSynchronization(synch);
|
||||
assertThat(TransactionSynchronizationManager.getCurrentTransactionName()).isEqualTo("txName");
|
||||
assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
|
||||
status.setRollbackOnly();
|
||||
assertThat(status.isRollbackOnly()).isTrue();
|
||||
assertThat(status.isCompleted()).isFalse();
|
||||
}
|
||||
});
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
|
||||
@@ -192,9 +220,16 @@ public class JtaTransactionManagerTests {
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
assertThat(status.getTransactionName()).isEmpty();
|
||||
assertThat(status.hasTransaction()).isTrue();
|
||||
assertThat(status.isNewTransaction()).isTrue();
|
||||
assertThat(status.isNested()).isFalse();
|
||||
assertThat(status.isReadOnly()).isFalse();
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
|
||||
TransactionSynchronizationManager.registerSynchronization(synch);
|
||||
status.setRollbackOnly();
|
||||
assertThat(status.isRollbackOnly()).isTrue();
|
||||
assertThat(status.isCompleted()).isFalse();
|
||||
}
|
||||
});
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
|
||||
@@ -221,8 +256,16 @@ public class JtaTransactionManagerTests {
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
assertThat(status.getTransactionName()).isEmpty();
|
||||
assertThat(status.hasTransaction()).isTrue();
|
||||
assertThat(status.isNewTransaction()).isTrue();
|
||||
assertThat(status.isNested()).isFalse();
|
||||
assertThat(status.isReadOnly()).isFalse();
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
|
||||
assertThat(status.isRollbackOnly()).isFalse();
|
||||
status.setRollbackOnly();
|
||||
assertThat(status.isRollbackOnly()).isTrue();
|
||||
assertThat(status.isCompleted()).isFalse();
|
||||
}
|
||||
});
|
||||
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
|
||||
@@ -808,6 +851,13 @@ public class JtaTransactionManagerTests {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
// something transactional
|
||||
assertThat(status.getTransactionName()).isEmpty();
|
||||
assertThat(status.hasTransaction()).isTrue();
|
||||
assertThat(status.isNewTransaction()).isTrue();
|
||||
assertThat(status.isNested()).isTrue();
|
||||
assertThat(status.isReadOnly()).isFalse();
|
||||
assertThat(status.isRollbackOnly()).isFalse();
|
||||
assertThat(status.isCompleted()).isFalse();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -859,8 +909,11 @@ public class JtaTransactionManagerTests {
|
||||
given(ut.getStatus()).willReturn(Status.STATUS_NO_TRANSACTION);
|
||||
willThrow(new SystemException("system exception")).given(ut).begin();
|
||||
|
||||
JtaTransactionManager ptm = newJtaTransactionManager(ut);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
ptm.addListener(tl);
|
||||
|
||||
assertThatExceptionOfType(CannotCreateTransactionException.class).isThrownBy(() -> {
|
||||
JtaTransactionManager ptm = newJtaTransactionManager(ut);
|
||||
TransactionTemplate tt = new TransactionTemplate(ptm);
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
@@ -869,6 +922,16 @@ public class JtaTransactionManagerTests {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beginFailure).isInstanceOf(CannotCreateTransactionException.class);
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.commitFailure).isNull();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
assertThat(tl.rollbackFailure).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -878,8 +941,11 @@ public class JtaTransactionManagerTests {
|
||||
Status.STATUS_ACTIVE, Status.STATUS_ACTIVE);
|
||||
willThrow(new RollbackException("unexpected rollback")).given(ut).commit();
|
||||
|
||||
JtaTransactionManager ptm = newJtaTransactionManager(ut);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
ptm.addListener(tl);
|
||||
|
||||
assertThatExceptionOfType(UnexpectedRollbackException.class).isThrownBy(() -> {
|
||||
JtaTransactionManager ptm = newJtaTransactionManager(ut);
|
||||
TransactionTemplate tt = new TransactionTemplate(ptm);
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
@@ -896,6 +962,16 @@ public class JtaTransactionManagerTests {
|
||||
});
|
||||
});
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beginFailure).isNull();
|
||||
assertThat(tl.beforeCommitCalled).isTrue();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.commitFailure).isNull();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isTrue();
|
||||
assertThat(tl.rollbackFailure).isNull();
|
||||
|
||||
verify(ut).begin();
|
||||
}
|
||||
|
||||
@@ -1023,8 +1099,11 @@ public class JtaTransactionManagerTests {
|
||||
Status.STATUS_ACTIVE, Status.STATUS_ACTIVE);
|
||||
willThrow(new SystemException("system exception")).given(ut).commit();
|
||||
|
||||
JtaTransactionManager ptm = newJtaTransactionManager(ut);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
ptm.addListener(tl);
|
||||
|
||||
assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() -> {
|
||||
JtaTransactionManager ptm = newJtaTransactionManager(ut);
|
||||
TransactionTemplate tt = new TransactionTemplate(ptm);
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
@@ -1041,6 +1120,16 @@ public class JtaTransactionManagerTests {
|
||||
});
|
||||
});
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beginFailure).isNull();
|
||||
assertThat(tl.beforeCommitCalled).isTrue();
|
||||
assertThat(tl.afterCommitCalled).isTrue();
|
||||
assertThat(tl.commitFailure).isInstanceOf(TransactionSystemException.class);
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
assertThat(tl.rollbackFailure).isNull();
|
||||
|
||||
verify(ut).begin();
|
||||
}
|
||||
|
||||
@@ -1050,8 +1139,11 @@ public class JtaTransactionManagerTests {
|
||||
given(ut.getStatus()).willReturn(Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE);
|
||||
willThrow(new SystemException("system exception")).given(ut).rollback();
|
||||
|
||||
JtaTransactionManager ptm = newJtaTransactionManager(ut);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
ptm.addListener(tl);
|
||||
|
||||
assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() -> {
|
||||
JtaTransactionManager ptm = newJtaTransactionManager(ut);
|
||||
TransactionTemplate tt = new TransactionTemplate(ptm);
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
@@ -1068,6 +1160,16 @@ public class JtaTransactionManagerTests {
|
||||
});
|
||||
});
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beginFailure).isNull();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.commitFailure).isNull();
|
||||
assertThat(tl.beforeRollbackCalled).isTrue();
|
||||
assertThat(tl.afterRollbackCalled).isTrue();
|
||||
assertThat(tl.rollbackFailure).isInstanceOf(TransactionSystemException.class);
|
||||
|
||||
verify(ut).begin();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.transaction;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 6.1
|
||||
*/
|
||||
public class TestTransactionExecutionListener implements TransactionExecutionListener {
|
||||
|
||||
public boolean beforeBeginCalled;
|
||||
|
||||
public boolean afterBeginCalled;
|
||||
|
||||
@Nullable
|
||||
public Throwable beginFailure;
|
||||
|
||||
public boolean beforeCommitCalled;
|
||||
|
||||
public boolean afterCommitCalled;
|
||||
|
||||
@Nullable
|
||||
public Throwable commitFailure;
|
||||
|
||||
public boolean beforeRollbackCalled;
|
||||
|
||||
public boolean afterRollbackCalled;
|
||||
|
||||
@Nullable
|
||||
public Throwable rollbackFailure;
|
||||
|
||||
|
||||
@Override
|
||||
public void beforeBegin(TransactionExecution transactionState) {
|
||||
this.beforeBeginCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterBegin(TransactionExecution transactionState, @Nullable Throwable beginFailure) {
|
||||
this.afterBeginCalled = true;
|
||||
this.beginFailure = beginFailure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeCommit(TransactionExecution transactionState) {
|
||||
this.beforeCommitCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCommit(TransactionExecution transactionState, @Nullable Throwable commitFailure) {
|
||||
this.afterCommitCalled = true;
|
||||
this.commitFailure = commitFailure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeRollback(TransactionExecution transactionState) {
|
||||
this.beforeRollbackCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRollback(TransactionExecution transactionState, @Nullable Throwable rollbackFailure) {
|
||||
this.afterRollbackCalled = true;
|
||||
this.rollbackFailure = rollbackFailure;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package org.springframework.transaction.reactive;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.CannotCreateTransactionException;
|
||||
import org.springframework.transaction.ReactiveTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
@@ -26,6 +29,7 @@ import org.springframework.transaction.TransactionDefinition;
|
||||
* Test implementation of a {@link ReactiveTransactionManager}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class ReactiveTestTransactionManager extends AbstractReactiveTransactionManager {
|
||||
@@ -36,7 +40,11 @@ class ReactiveTestTransactionManager extends AbstractReactiveTransactionManager
|
||||
|
||||
private final boolean canCreateTransaction;
|
||||
|
||||
private final boolean forceFailOnCommit;
|
||||
@Nullable
|
||||
private Function<String, RuntimeException> forceFailOnCommit;
|
||||
|
||||
@Nullable
|
||||
private Function<String, RuntimeException> forceFailOnRollback;
|
||||
|
||||
protected boolean begin = false;
|
||||
|
||||
@@ -50,13 +58,15 @@ class ReactiveTestTransactionManager extends AbstractReactiveTransactionManager
|
||||
|
||||
|
||||
ReactiveTestTransactionManager(boolean existingTransaction, boolean canCreateTransaction) {
|
||||
this(existingTransaction, canCreateTransaction, false);
|
||||
}
|
||||
|
||||
ReactiveTestTransactionManager(boolean existingTransaction, boolean canCreateTransaction, boolean forceFailOnCommit) {
|
||||
this.existingTransaction = existingTransaction;
|
||||
this.canCreateTransaction = canCreateTransaction;
|
||||
}
|
||||
|
||||
ReactiveTestTransactionManager(boolean existingTransaction, @Nullable Function<String, RuntimeException> forceFailOnCommit, @Nullable Function<String, RuntimeException> forceFailOnRollback) {
|
||||
this.existingTransaction = existingTransaction;
|
||||
this.canCreateTransaction = true;
|
||||
this.forceFailOnCommit = forceFailOnCommit;
|
||||
this.forceFailOnRollback = forceFailOnRollback;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,8 +98,8 @@ class ReactiveTestTransactionManager extends AbstractReactiveTransactionManager
|
||||
}
|
||||
return Mono.fromRunnable(() -> {
|
||||
this.commit = true;
|
||||
if (this.forceFailOnCommit) {
|
||||
throw new IllegalArgumentException("Forced failure on commit");
|
||||
if (this.forceFailOnCommit != null) {
|
||||
throw this.forceFailOnCommit.apply("Forced failure on commit");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -99,7 +109,12 @@ class ReactiveTestTransactionManager extends AbstractReactiveTransactionManager
|
||||
if (!TRANSACTION.equals(status.getTransaction())) {
|
||||
return Mono.error(new IllegalArgumentException("Not the same transaction object"));
|
||||
}
|
||||
return Mono.fromRunnable(() -> this.rollback = true);
|
||||
return Mono.fromRunnable(() -> {
|
||||
this.rollback = true;
|
||||
if (this.forceFailOnRollback != null) {
|
||||
throw this.forceFailOnRollback.apply("Forced failure on rollback");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,10 +21,14 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.transaction.CannotCreateTransactionException;
|
||||
import org.springframework.transaction.IllegalTransactionStateException;
|
||||
import org.springframework.transaction.ReactiveTransaction;
|
||||
import org.springframework.transaction.ReactiveTransactionManager;
|
||||
import org.springframework.transaction.TestTransactionExecutionListener;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -86,6 +90,9 @@ public class ReactiveTransactionSupportTests {
|
||||
@Test
|
||||
public void commitWithoutExistingTransaction() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(false, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).flatMap(tm::commit)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext())
|
||||
.as(StepVerifier::create).verifyComplete();
|
||||
@@ -95,11 +102,21 @@ public class ReactiveTransactionSupportTests {
|
||||
assertHasNoRollback(tm);
|
||||
assertHasNotSetRollbackOnly(tm);
|
||||
assertHasCleanedUp(tm);
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beforeCommitCalled).isTrue();
|
||||
assertThat(tl.afterCommitCalled).isTrue();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rollbackWithoutExistingTransaction() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(false, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).flatMap(tm::rollback)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext()).as(StepVerifier::create)
|
||||
.verifyComplete();
|
||||
@@ -109,11 +126,21 @@ public class ReactiveTransactionSupportTests {
|
||||
assertHasRolledBack(tm);
|
||||
assertHasNotSetRollbackOnly(tm);
|
||||
assertHasCleanedUp(tm);
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isTrue();
|
||||
assertThat(tl.afterRollbackCalled).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rollbackOnlyWithoutExistingTransaction() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(false, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).doOnNext(ReactiveTransaction::setRollbackOnly)
|
||||
.flatMap(tm::commit)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext()).as(StepVerifier::create)
|
||||
@@ -124,11 +151,21 @@ public class ReactiveTransactionSupportTests {
|
||||
assertHasRolledBack(tm);
|
||||
assertHasNotSetRollbackOnly(tm);
|
||||
assertHasCleanedUp(tm);
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isTrue();
|
||||
assertThat(tl.afterRollbackCalled).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commitWithExistingTransaction() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(true, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).flatMap(tm::commit)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext())
|
||||
.as(StepVerifier::create).verifyComplete();
|
||||
@@ -138,11 +175,21 @@ public class ReactiveTransactionSupportTests {
|
||||
assertHasNoRollback(tm);
|
||||
assertHasNotSetRollbackOnly(tm);
|
||||
assertHasNotCleanedUp(tm);
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isFalse();
|
||||
assertThat(tl.afterBeginCalled).isFalse();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rollbackWithExistingTransaction() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(true, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).flatMap(tm::rollback)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext()).as(StepVerifier::create)
|
||||
.verifyComplete();
|
||||
@@ -152,11 +199,21 @@ public class ReactiveTransactionSupportTests {
|
||||
assertHasNoRollback(tm);
|
||||
assertHasSetRollbackOnly(tm);
|
||||
assertHasNotCleanedUp(tm);
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isFalse();
|
||||
assertThat(tl.afterBeginCalled).isFalse();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rollbackOnlyWithExistingTransaction() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(true, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).doOnNext(ReactiveTransaction::setRollbackOnly).flatMap(tm::commit)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext()).as(StepVerifier::create)
|
||||
.verifyComplete();
|
||||
@@ -166,6 +223,13 @@ public class ReactiveTransactionSupportTests {
|
||||
assertHasNoRollback(tm);
|
||||
assertHasSetRollbackOnly(tm);
|
||||
assertHasNotCleanedUp(tm);
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isFalse();
|
||||
assertThat(tl.afterBeginCalled).isFalse();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -203,10 +267,11 @@ public class ReactiveTransactionSupportTests {
|
||||
assertHasCleanedUp(tm);
|
||||
}
|
||||
|
||||
//gh-28968
|
||||
@Test
|
||||
@Test // gh-28968
|
||||
void errorInCommitDoesInitiateRollbackAfterCommit() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(false, true, true);
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(false, ConcurrencyFailureException::new, null);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
TransactionalOperator rxtx = TransactionalOperator.create(tm);
|
||||
|
||||
StepVerifier.create(rxtx.transactional(Mono.just("bar")))
|
||||
@@ -217,8 +282,79 @@ public class ReactiveTransactionSupportTests {
|
||||
assertHasRolledBack(tm);
|
||||
assertHasNotSetRollbackOnly(tm);
|
||||
assertHasCleanedUp(tm);
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beforeCommitCalled).isTrue();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beginFailure() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(false, false);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).flatMap(tm::commit)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext())
|
||||
.as(StepVerifier::create).verifyError();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beginFailure).isInstanceOf(CannotCreateTransactionException.class);
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.commitFailure).isNull();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
assertThat(tl.rollbackFailure).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commitFailure() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(false, TransactionSystemException::new, null);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).flatMap(tm::commit)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext())
|
||||
.as(StepVerifier::create).verifyError();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beginFailure).isNull();
|
||||
assertThat(tl.beforeCommitCalled).isTrue();
|
||||
assertThat(tl.afterCommitCalled).isTrue();
|
||||
assertThat(tl.commitFailure).isInstanceOf(TransactionSystemException.class);
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
assertThat(tl.rollbackFailure).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rollbackFailure() {
|
||||
ReactiveTestTransactionManager tm = new ReactiveTestTransactionManager(false, null, TransactionSystemException::new);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
tm.getReactiveTransaction(new DefaultTransactionDefinition()).flatMap(tm::rollback)
|
||||
.contextWrite(TransactionContextManager.createTransactionContext())
|
||||
.as(StepVerifier::create).verifyError();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beginFailure).isNull();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.commitFailure).isNull();
|
||||
assertThat(tl.beforeRollbackCalled).isTrue();
|
||||
assertThat(tl.afterRollbackCalled).isTrue();
|
||||
assertThat(tl.rollbackFailure).isInstanceOf(TransactionSystemException.class);
|
||||
}
|
||||
|
||||
|
||||
private void assertHasBegan(ReactiveTestTransactionManager actual) {
|
||||
assertThat(actual.begin).as("Expected <ReactiveTransactionManager.begin()> but was <begin()> was not invoked").isTrue();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.transaction.IllegalTransactionStateException;
|
||||
import org.springframework.transaction.MockCallbackPreferringTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TestTransactionExecutionListener;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
@@ -63,6 +64,7 @@ class TransactionSupportTests {
|
||||
@Test
|
||||
void noExistingTransaction() {
|
||||
PlatformTransactionManager tm = new TestTransactionManager(false, true);
|
||||
|
||||
DefaultTransactionStatus status1 = (DefaultTransactionStatus)
|
||||
tm.getTransaction(new DefaultTransactionDefinition(PROPAGATION_SUPPORTS));
|
||||
assertThat(status1.hasTransaction()).as("Must not have transaction").isFalse();
|
||||
@@ -72,13 +74,14 @@ class TransactionSupportTests {
|
||||
assertThat(status2.hasTransaction()).as("Must have transaction").isTrue();
|
||||
assertThat(status2.isNewTransaction()).as("Must be new transaction").isTrue();
|
||||
|
||||
assertThatExceptionOfType(IllegalTransactionStateException.class).isThrownBy(() ->
|
||||
tm.getTransaction(new DefaultTransactionDefinition(PROPAGATION_MANDATORY)));
|
||||
assertThatExceptionOfType(IllegalTransactionStateException.class)
|
||||
.isThrownBy(() -> tm.getTransaction(new DefaultTransactionDefinition(PROPAGATION_MANDATORY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void existingTransaction() {
|
||||
PlatformTransactionManager tm = new TestTransactionManager(true, true);
|
||||
|
||||
DefaultTransactionStatus status1 = (DefaultTransactionStatus)
|
||||
tm.getTransaction(new DefaultTransactionDefinition(PROPAGATION_SUPPORTS));
|
||||
assertThat(status1.getTransaction()).as("Must have transaction").isNotNull();
|
||||
@@ -98,6 +101,9 @@ class TransactionSupportTests {
|
||||
@Test
|
||||
void commitWithoutExistingTransaction() {
|
||||
TestTransactionManager tm = new TestTransactionManager(false, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
TransactionStatus status = tm.getTransaction(null);
|
||||
tm.commit(status);
|
||||
|
||||
@@ -105,11 +111,21 @@ class TransactionSupportTests {
|
||||
assertThat(tm.commit).as("triggered commit").isTrue();
|
||||
assertThat(tm.rollback).as("no rollback").isFalse();
|
||||
assertThat(tm.rollbackOnly).as("no rollbackOnly").isFalse();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beforeCommitCalled).isTrue();
|
||||
assertThat(tl.afterCommitCalled).isTrue();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rollbackWithoutExistingTransaction() {
|
||||
TestTransactionManager tm = new TestTransactionManager(false, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
TransactionStatus status = tm.getTransaction(null);
|
||||
tm.rollback(status);
|
||||
|
||||
@@ -117,11 +133,21 @@ class TransactionSupportTests {
|
||||
assertThat(tm.commit).as("no commit").isFalse();
|
||||
assertThat(tm.rollback).as("triggered rollback").isTrue();
|
||||
assertThat(tm.rollbackOnly).as("no rollbackOnly").isFalse();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isTrue();
|
||||
assertThat(tl.afterRollbackCalled).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rollbackOnlyWithoutExistingTransaction() {
|
||||
TestTransactionManager tm = new TestTransactionManager(false, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
TransactionStatus status = tm.getTransaction(null);
|
||||
status.setRollbackOnly();
|
||||
tm.commit(status);
|
||||
@@ -130,11 +156,21 @@ class TransactionSupportTests {
|
||||
assertThat(tm.commit).as("no commit").isFalse();
|
||||
assertThat(tm.rollback).as("triggered rollback").isTrue();
|
||||
assertThat(tm.rollbackOnly).as("no rollbackOnly").isFalse();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isTrue();
|
||||
assertThat(tl.afterBeginCalled).isTrue();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isTrue();
|
||||
assertThat(tl.afterRollbackCalled).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void commitWithExistingTransaction() {
|
||||
TestTransactionManager tm = new TestTransactionManager(true, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
TransactionStatus status = tm.getTransaction(null);
|
||||
tm.commit(status);
|
||||
|
||||
@@ -142,11 +178,21 @@ class TransactionSupportTests {
|
||||
assertThat(tm.commit).as("no commit").isFalse();
|
||||
assertThat(tm.rollback).as("no rollback").isFalse();
|
||||
assertThat(tm.rollbackOnly).as("no rollbackOnly").isFalse();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isFalse();
|
||||
assertThat(tl.afterBeginCalled).isFalse();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rollbackWithExistingTransaction() {
|
||||
TestTransactionManager tm = new TestTransactionManager(true, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
TransactionStatus status = tm.getTransaction(null);
|
||||
tm.rollback(status);
|
||||
|
||||
@@ -154,11 +200,21 @@ class TransactionSupportTests {
|
||||
assertThat(tm.commit).as("no commit").isFalse();
|
||||
assertThat(tm.rollback).as("no rollback").isFalse();
|
||||
assertThat(tm.rollbackOnly).as("triggered rollbackOnly").isTrue();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isFalse();
|
||||
assertThat(tl.afterBeginCalled).isFalse();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rollbackOnlyWithExistingTransaction() {
|
||||
TestTransactionManager tm = new TestTransactionManager(true, true);
|
||||
TestTransactionExecutionListener tl = new TestTransactionExecutionListener();
|
||||
tm.addListener(tl);
|
||||
|
||||
TransactionStatus status = tm.getTransaction(null);
|
||||
status.setRollbackOnly();
|
||||
tm.commit(status);
|
||||
@@ -167,6 +223,13 @@ class TransactionSupportTests {
|
||||
assertThat(tm.commit).as("no commit").isFalse();
|
||||
assertThat(tm.rollback).as("no rollback").isFalse();
|
||||
assertThat(tm.rollbackOnly).as("triggered rollbackOnly").isTrue();
|
||||
|
||||
assertThat(tl.beforeBeginCalled).isFalse();
|
||||
assertThat(tl.afterBeginCalled).isFalse();
|
||||
assertThat(tl.beforeCommitCalled).isFalse();
|
||||
assertThat(tl.afterCommitCalled).isFalse();
|
||||
assertThat(tl.beforeRollbackCalled).isFalse();
|
||||
assertThat(tl.afterRollbackCalled).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -204,14 +267,14 @@ class TransactionSupportTests {
|
||||
TestTransactionManager tm = new TestTransactionManager(false, true);
|
||||
TransactionTemplate template = new TransactionTemplate(tm);
|
||||
RuntimeException ex = new RuntimeException("Some application exception");
|
||||
assertThatRuntimeException().isThrownBy(() ->
|
||||
template.execute(new TransactionCallbackWithoutResult() {
|
||||
assertThatRuntimeException()
|
||||
.isThrownBy(() -> template.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
throw ex;
|
||||
}
|
||||
}))
|
||||
.isSameAs(ex);
|
||||
.isSameAs(ex);
|
||||
assertThat(tm.begin).as("triggered begin").isTrue();
|
||||
assertThat(tm.commit).as("no commit").isFalse();
|
||||
assertThat(tm.rollback).as("triggered rollback").isTrue();
|
||||
@@ -232,8 +295,8 @@ class TransactionSupportTests {
|
||||
TransactionTemplate template = new TransactionTemplate(tm);
|
||||
RuntimeException ex = new RuntimeException("Some application exception");
|
||||
assertThatRuntimeException()
|
||||
.isThrownBy(() -> template.executeWithoutResult(status -> { throw ex; }))
|
||||
.isSameAs(tex);
|
||||
.isThrownBy(() -> template.executeWithoutResult(status -> { throw ex; }))
|
||||
.isSameAs(tex);
|
||||
assertThat(tm.begin).as("triggered begin").isTrue();
|
||||
assertThat(tm.commit).as("no commit").isFalse();
|
||||
assertThat(tm.rollback).as("triggered rollback").isTrue();
|
||||
@@ -245,7 +308,7 @@ class TransactionSupportTests {
|
||||
TestTransactionManager tm = new TestTransactionManager(false, true);
|
||||
TransactionTemplate template = new TransactionTemplate(tm);
|
||||
assertThatExceptionOfType(Error.class)
|
||||
.isThrownBy(() -> template.executeWithoutResult(status -> { throw new Error("Some application error"); }));
|
||||
.isThrownBy(() -> template.executeWithoutResult(status -> { throw new Error("Some application error"); }));
|
||||
assertThat(tm.begin).as("triggered begin").isTrue();
|
||||
assertThat(tm.commit).as("no commit").isFalse();
|
||||
assertThat(tm.rollback).as("triggered rollback").isTrue();
|
||||
@@ -265,12 +328,12 @@ class TransactionSupportTests {
|
||||
assertThat(template3).isEqualTo(template2);
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class AbstractPlatformTransactionManagerConfigurationTests {
|
||||
|
||||
private final AbstractPlatformTransactionManager tm = new TestTransactionManager(false, true);
|
||||
|
||||
|
||||
@Test
|
||||
void setTransactionSynchronizationNameToUnsupportedValues() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> tm.setTransactionSynchronizationName(null));
|
||||
@@ -302,22 +365,20 @@ class TransactionSupportTests {
|
||||
assertThat(tm.getTransactionSynchronization()).isEqualTo(SYNCHRONIZATION_ON_ACTUAL_TRANSACTION);
|
||||
}
|
||||
|
||||
|
||||
private static Stream<String> streamSynchronizationConstants() {
|
||||
return Arrays.stream(AbstractPlatformTransactionManager.class.getFields())
|
||||
.filter(ReflectionUtils::isPublicStaticFinal)
|
||||
.map(Field::getName)
|
||||
.filter(name -> name.startsWith("SYNCHRONIZATION_"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class TransactionTemplateConfigurationTests {
|
||||
|
||||
private final TransactionTemplate template = new TransactionTemplate();
|
||||
|
||||
|
||||
@Test
|
||||
void setTransactionManager() {
|
||||
TestTransactionManager tm = new TestTransactionManager(false, true);
|
||||
@@ -422,7 +483,6 @@ class TransactionSupportTests {
|
||||
.filter(ReflectionUtils::isPublicStaticFinal)
|
||||
.map(Field::getName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user