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:
Juergen Hoeller
2023-08-06 14:03:44 +02:00
parent eaf54b54c3
commit 3253d2de89
22 changed files with 1372 additions and 481 deletions

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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);
}
}
}