Skip to content

Commit 3253d2d

Browse files
committed
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
1 parent eaf54b5 commit 3253d2d

22 files changed

+1371
-480
lines changed

Diff for: spring-jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java

-5
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,6 @@ public boolean isSavepointAllowed() {
131131
return this.savepointAllowed;
132132
}
133133

134-
@Override
135-
public void flush() {
136-
// no-op
137-
}
138-
139134

140135
//---------------------------------------------------------------------
141136
// Implementation of SavepointManager

Diff for: spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java

+341-253
Large diffs are not rendered by default.

Diff for: spring-jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java

-5
Original file line numberDiff line numberDiff line change
@@ -448,11 +448,6 @@ public boolean hasResourceHolder() {
448448
public boolean isRollbackOnly() {
449449
return (this.resourceHolder != null && this.resourceHolder.isRollbackOnly());
450450
}
451-
452-
@Override
453-
public void flush() {
454-
// no-op
455-
}
456451
}
457452

458453
}

Diff for: spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java

+116-82
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ void testSimpleTransaction() {
8888

8989
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
9090
.flatMap(connection -> TransactionSynchronizationManager.forCurrentTransaction()
91-
.doOnNext(synchronizationManager -> synchronizationManager.registerSynchronization(
92-
sync)))
91+
.doOnNext(synchronizationManager -> synchronizationManager.registerSynchronization(sync)))
9392
.as(operator::transactional)
9493
.as(StepVerifier::create)
9594
.expectNextCount(1)
@@ -120,12 +119,11 @@ void testBeginFails() {
120119

121120
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
122121

123-
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
124-
operator::transactional)
122+
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
123+
.as(operator::transactional)
125124
.as(StepVerifier::create)
126125
.expectErrorSatisfies(actual -> assertThat(actual).isInstanceOf(
127-
CannotCreateTransactionException.class).hasCauseInstanceOf(
128-
R2dbcBadGrammarException.class))
126+
CannotCreateTransactionException.class).hasCauseInstanceOf(R2dbcBadGrammarException.class))
129127
.verify();
130128
}
131129

@@ -140,12 +138,16 @@ void appliesTransactionDefinition() {
140138
definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
141139

142140
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
143-
144-
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
145-
operator::transactional)
146-
.as(StepVerifier::create)
147-
.expectNextCount(1)
148-
.verifyComplete();
141+
operator.execute(tx -> {
142+
assertThat(tx.getTransactionName()).isEqualTo("my-transaction");
143+
assertThat(tx.hasTransaction()).isTrue();
144+
assertThat(tx.isNewTransaction()).isTrue();
145+
assertThat(tx.isNested()).isFalse();
146+
assertThat(tx.isReadOnly()).isTrue();
147+
assertThat(tx.isRollbackOnly()).isFalse();
148+
assertThat(tx.isCompleted()).isFalse();
149+
return Mono.empty();
150+
}).as(StepVerifier::create).verifyComplete();
149151

150152
ArgumentCaptor<io.r2dbc.spi.TransactionDefinition> txCaptor = ArgumentCaptor.forClass(io.r2dbc.spi.TransactionDefinition.class);
151153
verify(connectionMock).beginTransaction(txCaptor.capture());
@@ -171,8 +173,8 @@ void doesNotSetIsolationLevelIfMatch() {
171173

172174
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
173175

174-
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
175-
operator::transactional)
176+
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
177+
.as(operator::transactional)
176178
.as(StepVerifier::create)
177179
.expectNextCount(1)
178180
.verifyComplete();
@@ -190,8 +192,8 @@ void doesNotSetAutoCommitDisabled() {
190192

191193
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
192194

193-
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
194-
operator::transactional)
195+
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
196+
.as(operator::transactional)
195197
.as(StepVerifier::create)
196198
.expectNextCount(1)
197199
.verifyComplete();
@@ -215,8 +217,8 @@ void appliesReadOnly() {
215217

216218
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
217219

218-
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
219-
operator::transactional)
220+
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
221+
.as(operator::transactional)
220222
.as(StepVerifier::create)
221223
.expectNextCount(1)
222224
.verifyComplete();
@@ -262,11 +264,9 @@ void testRollback() {
262264
TransactionalOperator operator = TransactionalOperator.create(tm);
263265

264266
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
265-
.doOnNext(connection -> {
266-
throw new IllegalStateException();
267-
}).as(operator::transactional)
268-
.as(StepVerifier::create)
269-
.verifyError(IllegalStateException.class);
267+
.doOnNext(connection -> { throw new IllegalStateException(); })
268+
.as(operator::transactional)
269+
.as(StepVerifier::create).verifyError(IllegalStateException.class);
270270

271271
assertThat(commits).hasValue(0);
272272
assertThat(rollbacks).hasValue(1);
@@ -286,8 +286,7 @@ void testRollbackFails() {
286286
reactiveTransaction.setRollbackOnly();
287287
return ConnectionFactoryUtils.getConnection(connectionFactoryMock)
288288
.doOnNext(connection -> connection.createStatement("foo")).then();
289-
}).as(StepVerifier::create)
290-
.verifyError(BadSqlGrammarException.class);
289+
}).as(StepVerifier::create).verifyError(BadSqlGrammarException.class);
291290

292291
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
293292
verify(connectionMock).createStatement("foo");
@@ -308,7 +307,7 @@ void testConnectionReleasedWhenRollbackFails() {
308307
.doOnNext(connection -> {
309308
throw new IllegalStateException("Intentional error to trigger rollback");
310309
}).then()).as(StepVerifier::create)
311-
.verifyErrorSatisfies(e -> assertThat(e)
310+
.verifyErrorSatisfies(ex -> assertThat(ex)
312311
.isInstanceOf(BadSqlGrammarException.class)
313312
.hasCause(new R2dbcBadGrammarException("Rollback should fail"))
314313
);
@@ -327,15 +326,21 @@ void testTransactionSetRollbackOnly() {
327326

328327
TransactionalOperator operator = TransactionalOperator.create(tm);
329328
operator.execute(tx -> {
330-
tx.setRollbackOnly();
329+
assertThat(tx.getTransactionName()).isEmpty();
330+
assertThat(tx.hasTransaction()).isTrue();
331331
assertThat(tx.isNewTransaction()).isTrue();
332+
assertThat(tx.isNested()).isFalse();
333+
assertThat(tx.isReadOnly()).isFalse();
334+
assertThat(tx.isRollbackOnly()).isFalse();
335+
tx.setRollbackOnly();
336+
assertThat(tx.isRollbackOnly()).isTrue();
337+
assertThat(tx.isCompleted()).isFalse();
332338
return TransactionSynchronizationManager.forCurrentTransaction().doOnNext(
333339
synchronizationManager -> {
334340
assertThat(synchronizationManager.hasResource(connectionFactoryMock)).isTrue();
335341
synchronizationManager.registerSynchronization(sync);
336342
}).then();
337-
}).as(StepVerifier::create)
338-
.verifyComplete();
343+
}).as(StepVerifier::create).verifyComplete();
339344

340345
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
341346
verify(connectionMock).rollbackTransaction();
@@ -357,14 +362,19 @@ void testPropagationNeverWithExistingTransaction() {
357362

358363
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
359364
operator.execute(tx1 -> {
365+
assertThat(tx1.getTransactionName()).isEmpty();
366+
assertThat(tx1.hasTransaction()).isTrue();
360367
assertThat(tx1.isNewTransaction()).isTrue();
368+
assertThat(tx1.isNested()).isFalse();
369+
assertThat(tx1.isReadOnly()).isFalse();
370+
assertThat(tx1.isRollbackOnly()).isFalse();
371+
assertThat(tx1.isCompleted()).isFalse();
361372
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
362373
return operator.execute(tx2 -> {
363374
fail("Should have thrown IllegalTransactionStateException");
364375
return Mono.empty();
365376
});
366-
}).as(StepVerifier::create)
367-
.verifyError(IllegalTransactionStateException.class);
377+
}).as(StepVerifier::create).verifyError(IllegalTransactionStateException.class);
368378

369379
verify(connectionMock).rollbackTransaction();
370380
verify(connectionMock).close();
@@ -381,14 +391,17 @@ void testPropagationNestedWithExistingTransaction() {
381391

382392
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
383393
operator.execute(tx1 -> {
384-
assertThat(tx1.isNewTransaction()).isTrue();
385-
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
386-
return operator.execute(tx2 -> {
387-
assertThat(tx2.isNewTransaction()).isTrue();
388-
return Mono.empty();
389-
});
390-
}).as(StepVerifier::create)
391-
.verifyComplete();
394+
assertThat(tx1.hasTransaction()).isTrue();
395+
assertThat(tx1.isNewTransaction()).isTrue();
396+
assertThat(tx1.isNested()).isFalse();
397+
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
398+
return operator.execute(tx2 -> {
399+
assertThat(tx2.hasTransaction()).isTrue();
400+
assertThat(tx2.isNewTransaction()).isTrue();
401+
assertThat(tx2.isNested()).isTrue();
402+
return Mono.empty();
403+
});
404+
}).as(StepVerifier::create).verifyComplete();
392405

393406
verify(connectionMock).createSavepoint("SAVEPOINT_1");
394407
verify(connectionMock).releaseSavepoint("SAVEPOINT_1");
@@ -407,15 +420,20 @@ void testPropagationNestedWithExistingTransactionAndRollback() {
407420

408421
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
409422
operator.execute(tx1 -> {
410-
assertThat(tx1.isNewTransaction()).isTrue();
411-
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
412-
return operator.execute(tx2 -> {
413-
assertThat(tx2.isNewTransaction()).isTrue();
414-
tx2.setRollbackOnly();
415-
return Mono.empty();
416-
});
417-
}).as(StepVerifier::create)
418-
.verifyComplete();
423+
assertThat(tx1.hasTransaction()).isTrue();
424+
assertThat(tx1.isNewTransaction()).isTrue();
425+
assertThat(tx1.isNested()).isFalse();
426+
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
427+
return operator.execute(tx2 -> {
428+
assertThat(tx2.hasTransaction()).isTrue();
429+
assertThat(tx2.isNewTransaction()).isTrue();
430+
assertThat(tx2.isNested()).isTrue();
431+
assertThat(tx2.isRollbackOnly()).isFalse();
432+
tx2.setRollbackOnly();
433+
assertThat(tx2.isRollbackOnly()).isTrue();
434+
return Mono.empty();
435+
});
436+
}).as(StepVerifier::create).verifyComplete();
419437

420438
verify(connectionMock).createSavepoint("SAVEPOINT_1");
421439
verify(connectionMock).rollbackTransactionToSavepoint("SAVEPOINT_1");
@@ -432,16 +450,19 @@ void testPropagationSupportsAndNested() {
432450

433451
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
434452
operator.execute(tx1 -> {
435-
assertThat(tx1.isNewTransaction()).isFalse();
436-
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
437-
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
438-
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
439-
return inner.execute(tx2 -> {
440-
assertThat(tx2.isNewTransaction()).isTrue();
441-
return Mono.empty();
442-
});
443-
}).as(StepVerifier::create)
444-
.verifyComplete();
453+
assertThat(tx1.hasTransaction()).isFalse();
454+
assertThat(tx1.isNewTransaction()).isFalse();
455+
assertThat(tx1.isNested()).isFalse();
456+
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
457+
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
458+
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
459+
return inner.execute(tx2 -> {
460+
assertThat(tx2.hasTransaction()).isTrue();
461+
assertThat(tx2.isNewTransaction()).isTrue();
462+
assertThat(tx2.isNested()).isFalse();
463+
return Mono.empty();
464+
});
465+
}).as(StepVerifier::create).verifyComplete();
445466

446467
verify(connectionMock).commitTransaction();
447468
verify(connectionMock).close();
@@ -456,17 +477,22 @@ void testPropagationSupportsAndNestedWithRollback() {
456477

457478
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
458479
operator.execute(tx1 -> {
459-
assertThat(tx1.isNewTransaction()).isFalse();
460-
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
461-
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
462-
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
463-
return inner.execute(tx2 -> {
464-
assertThat(tx2.isNewTransaction()).isTrue();
465-
tx2.setRollbackOnly();
466-
return Mono.empty();
467-
});
468-
}).as(StepVerifier::create)
469-
.verifyComplete();
480+
assertThat(tx1.hasTransaction()).isFalse();
481+
assertThat(tx1.isNewTransaction()).isFalse();
482+
assertThat(tx1.isNested()).isFalse();
483+
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
484+
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
485+
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
486+
return inner.execute(tx2 -> {
487+
assertThat(tx2.hasTransaction()).isTrue();
488+
assertThat(tx2.isNewTransaction()).isTrue();
489+
assertThat(tx2.isNested()).isFalse();
490+
assertThat(tx2.isRollbackOnly()).isFalse();
491+
tx2.setRollbackOnly();
492+
assertThat(tx2.isRollbackOnly()).isTrue();
493+
return Mono.empty();
494+
});
495+
}).as(StepVerifier::create).verifyComplete();
470496

471497
verify(connectionMock).rollbackTransaction();
472498
verify(connectionMock).close();
@@ -481,16 +507,19 @@ void testPropagationSupportsAndRequiresNew() {
481507

482508
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
483509
operator.execute(tx1 -> {
510+
assertThat(tx1.hasTransaction()).isFalse();
484511
assertThat(tx1.isNewTransaction()).isFalse();
512+
assertThat(tx1.isNested()).isFalse();
485513
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
486514
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
487515
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
488516
return inner.execute(tx2 -> {
517+
assertThat(tx2.hasTransaction()).isTrue();
489518
assertThat(tx2.isNewTransaction()).isTrue();
519+
assertThat(tx2.isNested()).isFalse();
490520
return Mono.empty();
491521
});
492-
}).as(StepVerifier::create)
493-
.verifyComplete();
522+
}).as(StepVerifier::create).verifyComplete();
494523

495524
verify(connectionMock).commitTransaction();
496525
verify(connectionMock).close();
@@ -505,17 +534,22 @@ void testPropagationSupportsAndRequiresNewWithRollback() {
505534

506535
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
507536
operator.execute(tx1 -> {
508-
assertThat(tx1.isNewTransaction()).isFalse();
509-
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
510-
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
511-
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
512-
return inner.execute(tx2 -> {
513-
assertThat(tx2.isNewTransaction()).isTrue();
514-
tx2.setRollbackOnly();
515-
return Mono.empty();
516-
});
517-
}).as(StepVerifier::create)
518-
.verifyComplete();
537+
assertThat(tx1.hasTransaction()).isFalse();
538+
assertThat(tx1.isNewTransaction()).isFalse();
539+
assertThat(tx1.isNested()).isFalse();
540+
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
541+
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
542+
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
543+
return inner.execute(tx2 -> {
544+
assertThat(tx2.hasTransaction()).isTrue();
545+
assertThat(tx2.isNewTransaction()).isTrue();
546+
assertThat(tx2.isNested()).isFalse();
547+
assertThat(tx2.isRollbackOnly()).isFalse();
548+
tx2.setRollbackOnly();
549+
assertThat(tx2.isRollbackOnly()).isTrue();
550+
return Mono.empty();
551+
});
552+
}).as(StepVerifier::create).verifyComplete();
519553

520554
verify(connectionMock).rollbackTransaction();
521555
verify(connectionMock).close();

0 commit comments

Comments
 (0)