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

@@ -88,8 +88,7 @@ class R2dbcTransactionManagerUnitTests {
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.flatMap(connection -> TransactionSynchronizationManager.forCurrentTransaction()
.doOnNext(synchronizationManager -> synchronizationManager.registerSynchronization(
sync)))
.doOnNext(synchronizationManager -> synchronizationManager.registerSynchronization(sync)))
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
@@ -120,12 +119,11 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectErrorSatisfies(actual -> assertThat(actual).isInstanceOf(
CannotCreateTransactionException.class).hasCauseInstanceOf(
R2dbcBadGrammarException.class))
CannotCreateTransactionException.class).hasCauseInstanceOf(R2dbcBadGrammarException.class))
.verify();
}
@@ -140,12 +138,16 @@ class R2dbcTransactionManagerUnitTests {
definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
operator.execute(tx -> {
assertThat(tx.getTransactionName()).isEqualTo("my-transaction");
assertThat(tx.hasTransaction()).isTrue();
assertThat(tx.isNewTransaction()).isTrue();
assertThat(tx.isNested()).isFalse();
assertThat(tx.isReadOnly()).isTrue();
assertThat(tx.isRollbackOnly()).isFalse();
assertThat(tx.isCompleted()).isFalse();
return Mono.empty();
}).as(StepVerifier::create).verifyComplete();
ArgumentCaptor<io.r2dbc.spi.TransactionDefinition> txCaptor = ArgumentCaptor.forClass(io.r2dbc.spi.TransactionDefinition.class);
verify(connectionMock).beginTransaction(txCaptor.capture());
@@ -171,8 +173,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@@ -190,8 +192,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@@ -215,8 +217,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@@ -262,11 +264,9 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm);
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.doOnNext(connection -> {
throw new IllegalStateException();
}).as(operator::transactional)
.as(StepVerifier::create)
.verifyError(IllegalStateException.class);
.doOnNext(connection -> { throw new IllegalStateException(); })
.as(operator::transactional)
.as(StepVerifier::create).verifyError(IllegalStateException.class);
assertThat(commits).hasValue(0);
assertThat(rollbacks).hasValue(1);
@@ -286,8 +286,7 @@ class R2dbcTransactionManagerUnitTests {
reactiveTransaction.setRollbackOnly();
return ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.doOnNext(connection -> connection.createStatement("foo")).then();
}).as(StepVerifier::create)
.verifyError(BadSqlGrammarException.class);
}).as(StepVerifier::create).verifyError(BadSqlGrammarException.class);
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
verify(connectionMock).createStatement("foo");
@@ -308,7 +307,7 @@ class R2dbcTransactionManagerUnitTests {
.doOnNext(connection -> {
throw new IllegalStateException("Intentional error to trigger rollback");
}).then()).as(StepVerifier::create)
.verifyErrorSatisfies(e -> assertThat(e)
.verifyErrorSatisfies(ex -> assertThat(ex)
.isInstanceOf(BadSqlGrammarException.class)
.hasCause(new R2dbcBadGrammarException("Rollback should fail"))
);
@@ -327,15 +326,21 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm);
operator.execute(tx -> {
tx.setRollbackOnly();
assertThat(tx.getTransactionName()).isEmpty();
assertThat(tx.hasTransaction()).isTrue();
assertThat(tx.isNewTransaction()).isTrue();
assertThat(tx.isNested()).isFalse();
assertThat(tx.isReadOnly()).isFalse();
assertThat(tx.isRollbackOnly()).isFalse();
tx.setRollbackOnly();
assertThat(tx.isRollbackOnly()).isTrue();
assertThat(tx.isCompleted()).isFalse();
return TransactionSynchronizationManager.forCurrentTransaction().doOnNext(
synchronizationManager -> {
assertThat(synchronizationManager.hasResource(connectionFactoryMock)).isTrue();
synchronizationManager.registerSynchronization(sync);
}).then();
}).as(StepVerifier::create)
.verifyComplete();
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
verify(connectionMock).rollbackTransaction();
@@ -357,14 +362,19 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.getTransactionName()).isEmpty();
assertThat(tx1.hasTransaction()).isTrue();
assertThat(tx1.isNewTransaction()).isTrue();
assertThat(tx1.isNested()).isFalse();
assertThat(tx1.isReadOnly()).isFalse();
assertThat(tx1.isRollbackOnly()).isFalse();
assertThat(tx1.isCompleted()).isFalse();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
return operator.execute(tx2 -> {
fail("Should have thrown IllegalTransactionStateException");
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyError(IllegalTransactionStateException.class);
}).as(StepVerifier::create).verifyError(IllegalTransactionStateException.class);
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();
@@ -381,14 +391,17 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isTrue();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
return operator.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.hasTransaction()).isTrue();
assertThat(tx1.isNewTransaction()).isTrue();
assertThat(tx1.isNested()).isFalse();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
return operator.execute(tx2 -> {
assertThat(tx2.hasTransaction()).isTrue();
assertThat(tx2.isNewTransaction()).isTrue();
assertThat(tx2.isNested()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).createSavepoint("SAVEPOINT_1");
verify(connectionMock).releaseSavepoint("SAVEPOINT_1");
@@ -407,15 +420,20 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isTrue();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
return operator.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.hasTransaction()).isTrue();
assertThat(tx1.isNewTransaction()).isTrue();
assertThat(tx1.isNested()).isFalse();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
return operator.execute(tx2 -> {
assertThat(tx2.hasTransaction()).isTrue();
assertThat(tx2.isNewTransaction()).isTrue();
assertThat(tx2.isNested()).isTrue();
assertThat(tx2.isRollbackOnly()).isFalse();
tx2.setRollbackOnly();
assertThat(tx2.isRollbackOnly()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).createSavepoint("SAVEPOINT_1");
verify(connectionMock).rollbackTransactionToSavepoint("SAVEPOINT_1");
@@ -432,16 +450,19 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.hasTransaction()).isFalse();
assertThat(tx1.isNewTransaction()).isFalse();
assertThat(tx1.isNested()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.hasTransaction()).isTrue();
assertThat(tx2.isNewTransaction()).isTrue();
assertThat(tx2.isNested()).isFalse();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).commitTransaction();
verify(connectionMock).close();
@@ -456,17 +477,22 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.hasTransaction()).isFalse();
assertThat(tx1.isNewTransaction()).isFalse();
assertThat(tx1.isNested()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.hasTransaction()).isTrue();
assertThat(tx2.isNewTransaction()).isTrue();
assertThat(tx2.isNested()).isFalse();
assertThat(tx2.isRollbackOnly()).isFalse();
tx2.setRollbackOnly();
assertThat(tx2.isRollbackOnly()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();
@@ -481,16 +507,19 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.hasTransaction()).isFalse();
assertThat(tx1.isNewTransaction()).isFalse();
assertThat(tx1.isNested()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.hasTransaction()).isTrue();
assertThat(tx2.isNewTransaction()).isTrue();
assertThat(tx2.isNested()).isFalse();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).commitTransaction();
verify(connectionMock).close();
@@ -505,17 +534,22 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.hasTransaction()).isFalse();
assertThat(tx1.isNewTransaction()).isFalse();
assertThat(tx1.isNested()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.hasTransaction()).isTrue();
assertThat(tx2.isNewTransaction()).isTrue();
assertThat(tx2.isNested()).isFalse();
assertThat(tx2.isRollbackOnly()).isFalse();
tx2.setRollbackOnly();
assertThat(tx2.isRollbackOnly()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();