From a33027703d20735be8c640b732b8c830627f09db Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 2 Nov 2025 17:28:12 +0100 Subject: [PATCH] Preserve Connection readOnly state for defaultReadOnly DataSource Includes DataSourceTransactionManagerTests alignment with main branch. Closes gh-35743 --- .../DataSourceTransactionManager.java | 45 +- .../jdbc/datasource/DataSourceUtils.java | 34 +- .../LazyConnectionDataSourceProxy.java | 5 +- .../DataSourceTransactionManagerTests.java | 1307 ++++++++--------- .../support/JdbcTransactionManagerTests.java | 83 +- 5 files changed, 681 insertions(+), 793 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java index 58eb2bd6f0a..0fd07bb004a 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java @@ -126,6 +126,8 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan private boolean enforceReadOnly = false; + private volatile @Nullable Boolean defaultReadOnly; + /** * Create a new {@code DataSourceTransactionManager} instance. @@ -270,13 +272,18 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } + if (definition.isReadOnly()) { + checkDefaultReadOnly(newCon); + } txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con = txObject.getConnectionHolder().getConnection(); - Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); + Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, + definition.getIsolationLevel(), + (definition.isReadOnly() && !isDefaultReadOnly())); txObject.setPreviousIsolationLevel(previousIsolationLevel); txObject.setReadOnly(definition.isReadOnly()); @@ -381,8 +388,9 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan if (txObject.isMustRestoreAutoCommit()) { con.setAutoCommit(true); } - DataSourceUtils.resetConnectionAfterTransaction( - con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly()); + DataSourceUtils.resetConnectionAfterTransaction(con, + txObject.getPreviousIsolationLevel(), + (txObject.isReadOnly() && !isDefaultReadOnly())); } catch (Throwable ex) { logger.debug("Could not reset JDBC Connection after transaction", ex); @@ -399,6 +407,37 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan } + /** + * Check the default {@link Connection#isReadOnly()} flag on a freshly + * obtained connection from the {@code DataSource}, assuming that the + * same flag applies to all connections obtained from the given setup. + * @param newCon the Connection to check + * @since 6.2.13 + * @see #isDefaultReadOnly() + */ + private void checkDefaultReadOnly(Connection newCon) { + if (this.defaultReadOnly == null) { + try { + this.defaultReadOnly = newCon.isReadOnly(); + } + catch (Throwable ex) { + logger.debug("Could not determine default JDBC Connection isReadOnly - assuming false", ex); + this.defaultReadOnly = false; + } + } + } + + /** + * Check whether the default read-only flag has been determined as {@code true}, + * assuming that all encountered connections will be read-only by default and + * therefore do not need explicit {@link Connection#setReadOnly} (re)setting. + * @since 6.2.13 + * @see #checkDefaultReadOnly(Connection) + */ + private boolean isDefaultReadOnly() { + return (this.defaultReadOnly == Boolean.TRUE); + } + /** * Prepare the transactional {@code Connection} right after transaction begin. *

The default implementation executes a "SET TRANSACTION READ ONLY" statement diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java index 9783945522b..d40c4d4dcf3 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java @@ -170,19 +170,38 @@ public abstract class DataSourceUtils { * @param definition the transaction definition to apply * @return the previous isolation level, if any * @throws SQLException if thrown by JDBC methods - * @see #resetConnectionAfterTransaction + * @see #prepareConnectionForTransaction(Connection, int, boolean) + */ + @Nullable + public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition) + throws SQLException { + + return prepareConnectionForTransaction(con, + (definition != null ? definition.getIsolationLevel() : TransactionDefinition.ISOLATION_DEFAULT), + (definition != null && definition.isReadOnly())); + } + + /** + * Prepare the given Connection with the given transaction semantics. + * @param con the Connection to prepare + * @param isolationLevel the isolation level to apply + * @param setReadOnly whether to set the read-only flag + * @return the previous isolation level, if any + * @throws SQLException if thrown by JDBC methods + * @since 6.2.13 + * @see #resetConnectionAfterTransaction(Connection, Integer, boolean) * @see Connection#setTransactionIsolation * @see Connection#setReadOnly */ @Nullable - public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition) + static Integer prepareConnectionForTransaction(Connection con, int isolationLevel, boolean setReadOnly) throws SQLException { Assert.notNull(con, "No Connection specified"); boolean debugEnabled = logger.isDebugEnabled(); // Set read-only flag. - if (definition != null && definition.isReadOnly()) { + if (setReadOnly) { try { if (debugEnabled) { logger.debug("Setting JDBC Connection [" + con + "] read-only"); @@ -205,15 +224,14 @@ public abstract class DataSourceUtils { // Apply specific isolation level, if any. Integer previousIsolationLevel = null; - if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { + if (isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) { if (debugEnabled) { - logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " + - definition.getIsolationLevel()); + logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " + isolationLevel); } int currentIsolation = con.getTransactionIsolation(); - if (currentIsolation != definition.getIsolationLevel()) { + if (currentIsolation != isolationLevel) { previousIsolationLevel = currentIsolation; - con.setTransactionIsolation(definition.getIsolationLevel()); + con.setTransactionIsolation(isolationLevel); } } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxy.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxy.java index bd0354fd84e..46bb2d2fd06 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxy.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxy.java @@ -153,6 +153,9 @@ public class LazyConnectionDataSourceProxy extends DelegatingDataSource { */ public void setReadOnlyDataSource(@Nullable DataSource readOnlyDataSource) { this.readOnlyDataSource = readOnlyDataSource; + if (getTargetDataSource() == null) { + setTargetDataSource(readOnlyDataSource); + } } /** @@ -395,7 +398,7 @@ public class LazyConnectionDataSourceProxy extends DelegatingDataSource { return null; } case "isReadOnly" -> { - return this.readOnly; + return (this.readOnly || getTargetDataSource() == readOnlyDataSource); } case "setReadOnly" -> { this.readOnly = (Boolean) args[0]; diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java index bbcf092724a..8c5b3b7c7bb 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java @@ -44,7 +44,6 @@ import org.springframework.transaction.TransactionSystemException; import org.springframework.transaction.TransactionTimedOutException; import org.springframework.transaction.UnexpectedRollbackException; import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; @@ -98,36 +97,36 @@ public class DataSourceTransactionManagerTests { @Test - void testTransactionCommitWithAutoCommitTrue() throws Exception { - doTestTransactionCommitRestoringAutoCommit(true, false, false); + void transactionCommitWithAutoCommitTrue() throws Exception { + testTransactionCommitRestoringAutoCommit(true, false, false); } @Test - void testTransactionCommitWithAutoCommitFalse() throws Exception { - doTestTransactionCommitRestoringAutoCommit(false, false, false); + void transactionCommitWithAutoCommitFalse() throws Exception { + testTransactionCommitRestoringAutoCommit(false, false, false); } @Test - void testTransactionCommitWithAutoCommitTrueAndLazyConnection() throws Exception { - doTestTransactionCommitRestoringAutoCommit(true, true, false); + void transactionCommitWithAutoCommitTrueAndLazyConnection() throws Exception { + testTransactionCommitRestoringAutoCommit(true, true, false); } @Test - void testTransactionCommitWithAutoCommitFalseAndLazyConnection() throws Exception { - doTestTransactionCommitRestoringAutoCommit(false, true, false); + void transactionCommitWithAutoCommitFalseAndLazyConnection() throws Exception { + testTransactionCommitRestoringAutoCommit(false, true, false); } @Test - void testTransactionCommitWithAutoCommitTrueAndLazyConnectionAndStatementCreated() throws Exception { - doTestTransactionCommitRestoringAutoCommit(true, true, true); + void transactionCommitWithAutoCommitTrueAndLazyConnectionAndStatementCreated() throws Exception { + testTransactionCommitRestoringAutoCommit(true, true, true); } @Test - void testTransactionCommitWithAutoCommitFalseAndLazyConnectionAndStatementCreated() throws Exception { - doTestTransactionCommitRestoringAutoCommit(false, true, true); + void transactionCommitWithAutoCommitFalseAndLazyConnectionAndStatementCreated() throws Exception { + testTransactionCommitRestoringAutoCommit(false, true, true); } - private void doTestTransactionCommitRestoringAutoCommit( + private void testTransactionCommitRestoringAutoCommit( boolean autoCommit, boolean lazyConnection, boolean createStatement) throws Exception { given(con.getAutoCommit()).willReturn(autoCommit); @@ -143,28 +142,25 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - Connection tCon = DataSourceUtils.getConnection(dsToUse); - try { - if (createStatement) { - tCon.createStatement(); - } - else { - tCon.getWarnings(); - tCon.clearWarnings(); - } + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); + Connection tCon = DataSourceUtils.getConnection(dsToUse); + try { + if (createStatement) { + tCon.createStatement(); } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); + else { + tCon.getWarnings(); + tCon.clearWarnings(); } } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); + } }); assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isFalse(); @@ -185,36 +181,36 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionRollbackWithAutoCommitTrue() throws Exception { - doTestTransactionRollbackRestoringAutoCommit(true, false, false); + void transactionRollbackWithAutoCommitTrue() throws Exception { + testTransactionRollbackRestoringAutoCommit(true, false, false); } @Test - void testTransactionRollbackWithAutoCommitFalse() throws Exception { - doTestTransactionRollbackRestoringAutoCommit(false, false, false); + void transactionRollbackWithAutoCommitFalse() throws Exception { + testTransactionRollbackRestoringAutoCommit(false, false, false); } @Test - void testTransactionRollbackWithAutoCommitTrueAndLazyConnection() throws Exception { - doTestTransactionRollbackRestoringAutoCommit(true, true, false); + void transactionRollbackWithAutoCommitTrueAndLazyConnection() throws Exception { + testTransactionRollbackRestoringAutoCommit(true, true, false); } @Test - void testTransactionRollbackWithAutoCommitFalseAndLazyConnection() throws Exception { - doTestTransactionRollbackRestoringAutoCommit(false, true, false); + void transactionRollbackWithAutoCommitFalseAndLazyConnection() throws Exception { + testTransactionRollbackRestoringAutoCommit(false, true, false); } @Test - void testTransactionRollbackWithAutoCommitTrueAndLazyConnectionAndCreateStatement() throws Exception { - doTestTransactionRollbackRestoringAutoCommit(true, true, true); + void transactionRollbackWithAutoCommitTrueAndLazyConnectionAndCreateStatement() throws Exception { + testTransactionRollbackRestoringAutoCommit(true, true, true); } @Test - void testTransactionRollbackWithAutoCommitFalseAndLazyConnectionAndCreateStatement() throws Exception { - doTestTransactionRollbackRestoringAutoCommit(false, true, true); + void transactionRollbackWithAutoCommitFalseAndLazyConnectionAndCreateStatement() throws Exception { + testTransactionRollbackRestoringAutoCommit(false, true, true); } - private void doTestTransactionRollbackRestoringAutoCommit( + private void testTransactionRollbackRestoringAutoCommit( boolean autoCommit, boolean lazyConnection, boolean createStatement) throws Exception { given(con.getAutoCommit()).willReturn(autoCommit); @@ -231,23 +227,20 @@ public class DataSourceTransactionManagerTests { RuntimeException ex = new RuntimeException("Application exception"); assertThatRuntimeException().isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - Connection con = DataSourceUtils.getConnection(dsToUse); - if (createStatement) { - try { - con.createStatement(); - } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); - } + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + Connection con = DataSourceUtils.getConnection(dsToUse); + if (createStatement) { + try { + con.createStatement(); + } + catch (SQLException sqlException) { + throw new UncategorizedSQLException("", "", sqlException); } - throw ex; } + throw ex; })) .isEqualTo(ex); @@ -269,7 +262,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionRollbackOnly() { + void transactionRollbackOnly() { tm.setTransactionSynchronization(DataSourceTransactionManager.SYNCHRONIZATION_NEVER); TransactionTemplate tt = new TransactionTemplate(tm); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -279,14 +272,11 @@ public class DataSourceTransactionManagerTests { TransactionSynchronizationManager.bindResource(ds, conHolder); RuntimeException ex = new RuntimeException("Application exception"); try { - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - assertThat(status.isNewTransaction()).isFalse(); - throw ex; - } + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); + assertThat(status.isNewTransaction()).isFalse(); + throw ex; }); fail("Should have thrown RuntimeException"); } @@ -303,16 +293,16 @@ public class DataSourceTransactionManagerTests { } @Test - void testParticipatingTransactionWithRollbackOnly() throws Exception { - doTestParticipatingTransactionWithRollbackOnly(false); + void participatingTransactionWithRollbackOnly() throws Exception { + testParticipatingTransactionWithRollbackOnly(false); } @Test - void testParticipatingTransactionWithRollbackOnlyAndFailEarly() throws Exception { - doTestParticipatingTransactionWithRollbackOnly(true); + void participatingTransactionWithRollbackOnlyAndFailEarly() throws Exception { + testParticipatingTransactionWithRollbackOnly(true); } - private void doTestParticipatingTransactionWithRollbackOnly(boolean failEarly) throws Exception { + private void testParticipatingTransactionWithRollbackOnly(boolean failEarly) throws Exception { given(con.isReadOnly()).willReturn(false); if (failEarly) { tm.setFailEarlyOnGlobalRollbackOnly(true); @@ -330,23 +320,17 @@ public class DataSourceTransactionManagerTests { assertThat(ts.isNewTransaction()).isTrue(); TransactionTemplate tt = new TransactionTemplate(tm); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isRollbackOnly()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.isNewTransaction()).isFalse(); - status.setRollbackOnly(); - } - }); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isRollbackOnly()).isTrue(); - } + tt.executeWithoutResult(status1 -> { + assertThat(status1.isNewTransaction()).isFalse(); + assertThat(status1.isRollbackOnly()).isFalse(); + tt.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.isNewTransaction()).isFalse(); + status2.setRollbackOnly(); + }); + assertThat(status1.isNewTransaction()).isFalse(); + assertThat(status1.isRollbackOnly()).isTrue(); }); outerTransactionBoundaryReached = true; @@ -377,7 +361,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testParticipatingTransactionWithIncompatibleIsolationLevel() throws Exception { + void participatingTransactionWithIncompatibleIsolationLevel() throws Exception { tm.setValidateExistingTransaction(true); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -388,18 +372,10 @@ public class DataSourceTransactionManagerTests { TransactionTemplate tt2 = new TransactionTemplate(tm); tt2.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.isRollbackOnly()).isFalse(); - tt2.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - status.setRollbackOnly(); - } - }); - assertThat(status.isRollbackOnly()).isTrue(); - } + tt.executeWithoutResult(status -> { + assertThat(status.isRollbackOnly()).isFalse(); + tt2.executeWithoutResult(status2 -> status2.setRollbackOnly()); + assertThat(status.isRollbackOnly()).isTrue(); }); }); @@ -409,7 +385,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testParticipatingTransactionWithIncompatibleReadOnly() throws Exception { + void participatingTransactionWithIncompatibleReadOnly() throws Exception { willThrow(new SQLException("read-only not supported")).given(con).setReadOnly(true); tm.setValidateExistingTransaction(true); @@ -422,18 +398,10 @@ public class DataSourceTransactionManagerTests { TransactionTemplate tt2 = new TransactionTemplate(tm); tt2.setReadOnly(false); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.isRollbackOnly()).isFalse(); - tt2.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - status.setRollbackOnly(); - } - }); - assertThat(status.isRollbackOnly()).isTrue(); - } + tt.executeWithoutResult(status -> { + assertThat(status.isRollbackOnly()).isFalse(); + tt2.executeWithoutResult(status2 -> status2.setRollbackOnly()); + assertThat(status.isRollbackOnly()).isTrue(); }); }); @@ -443,7 +411,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testParticipatingTransactionWithTransactionStartedFromSynch() throws Exception { + void participatingTransactionWithTransactionStartedFromSynch() throws Exception { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); @@ -455,21 +423,14 @@ public class DataSourceTransactionManagerTests { @Override protected void doAfterCompletion(int status) { super.doAfterCompletion(status); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - } + tt.executeWithoutResult(status2 -> { }); TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {}); } }; - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - TransactionSynchronizationManager.registerSynchronization(synch); - } - }); + tt.executeWithoutResult(status -> + TransactionSynchronizationManager.registerSynchronization(synch)); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(synch.beforeCommitCalled).isTrue(); @@ -482,7 +443,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testParticipatingTransactionWithDifferentConnectionObtainedFromSynch() throws Exception { + void participatingTransactionWithDifferentConnectionObtainedFromSynch() throws Exception { DataSource ds2 = mock(); Connection con2 = mock(); given(ds2.getConnection()).willReturn(con2); @@ -502,12 +463,8 @@ public class DataSourceTransactionManagerTests { } }; - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - TransactionSynchronizationManager.registerSynchronization(synch); - } - }); + tt.executeWithoutResult(status -> + TransactionSynchronizationManager.registerSynchronization(synch)); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(synch.beforeCommitCalled).isTrue(); @@ -521,7 +478,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testParticipatingTransactionWithRollbackOnlyAndInnerSynch() throws Exception { + void participatingTransactionWithRollbackOnlyAndInnerSynch() throws Exception { tm.setTransactionSynchronization(DataSourceTransactionManager.SYNCHRONIZATION_NEVER); DataSourceTransactionManager tm2 = createTransactionManager(ds); // tm has no synch enabled (used at outer level), tm2 has synch enabled (inner level) @@ -536,24 +493,18 @@ public class DataSourceTransactionManagerTests { assertThatExceptionOfType(UnexpectedRollbackException.class).isThrownBy(() -> { assertThat(ts.isNewTransaction()).isTrue(); TransactionTemplate tt = new TransactionTemplate(tm2); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isRollbackOnly()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.isNewTransaction()).isFalse(); - status.setRollbackOnly(); - } - }); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isRollbackOnly()).isTrue(); - TransactionSynchronizationManager.registerSynchronization(synch); - } + tt.executeWithoutResult(status -> { + assertThat(status.isNewTransaction()).isFalse(); + assertThat(status.isRollbackOnly()).isFalse(); + tt.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.isNewTransaction()).isFalse(); + status2.setRollbackOnly(); + }); + assertThat(status.isNewTransaction()).isFalse(); + assertThat(status.isRollbackOnly()).isTrue(); + TransactionSynchronizationManager.registerSynchronization(synch); }); tm.commit(ts); @@ -569,34 +520,28 @@ public class DataSourceTransactionManagerTests { } @Test - void testPropagationRequiresNewWithExistingTransaction() throws Exception { + void propagationRequiresNewWithExistingTransaction() throws Exception { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.isNewTransaction()).isTrue(); + tt.executeWithoutResult(status -> { + assertThat(status.isNewTransaction()).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); + tt.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.isNewTransaction()).isTrue(); assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - status.setRollbackOnly(); - } - }); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - } + status2.setRollbackOnly(); + }); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -606,7 +551,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testPropagationRequiresNewWithExistingTransactionAndUnrelatedDataSource() throws Exception { + void propagationRequiresNewWithExistingTransactionAndUnrelatedDataSource() throws Exception { Connection con2 = mock(); DataSource ds2 = mock(); given(ds2.getConnection()).willReturn(con2); @@ -622,28 +567,22 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds2)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.isNewTransaction()).isTrue(); + tt.executeWithoutResult(status -> { + assertThat(status.isNewTransaction()).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); + tt2.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.isNewTransaction()).isTrue(); assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - tt2.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - status.setRollbackOnly(); - } - }); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - } + status2.setRollbackOnly(); + }); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -655,7 +594,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testPropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSource() throws Exception { + void propagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSource() throws Exception { DataSource ds2 = mock(); SQLException failure = new SQLException(); given(ds2.getConnection()).willThrow(failure); @@ -673,24 +612,16 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); assertThatExceptionOfType(CannotCreateTransactionException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.isReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - tt2.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - status.setRollbackOnly(); - } - }); - } - })).withCause(failure); + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.isReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); + tt2.executeWithoutResult(status2 -> status2.setRollbackOnly()); + })).withCause(failure); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.hasResource(ds2)).isFalse(); @@ -699,39 +630,33 @@ public class DataSourceTransactionManagerTests { } @Test - void testPropagationNotSupportedWithExistingTransaction() throws Exception { + void propagationNotSupportedWithExistingTransaction() throws Exception { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isFalse(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isFalse(); - assertThat(status.isReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse(); - status.setRollbackOnly(); - } - }); - assertThat(status.isNewTransaction()).isTrue(); + tt.executeWithoutResult(status -> { + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); + tt.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.hasTransaction()).isFalse(); + assertThat(status2.isNewTransaction()).isFalse(); + assertThat(status2.isNested()).isFalse(); + assertThat(status2.isReadOnly()).isFalse(); assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - } + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse(); + status2.setRollbackOnly(); + }); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -740,28 +665,21 @@ public class DataSourceTransactionManagerTests { } @Test - void testPropagationNeverWithExistingTransaction() throws Exception { + void propagationNeverWithExistingTransaction() throws Exception { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); assertThatExceptionOfType(IllegalTransactionStateException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - fail("Should have thrown IllegalTransactionStateException"); - } - }); - fail("Should have thrown IllegalTransactionStateException"); - } + tt.executeWithoutResult(status -> { + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER); + tt.executeWithoutResult(status2 -> + fail("Should have thrown IllegalTransactionStateException")); + fail("Should have thrown IllegalTransactionStateException"); })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -770,34 +688,28 @@ public class DataSourceTransactionManagerTests { } @Test - void testPropagationSupportsAndRequiresNew() throws Exception { + void propagationSupportsAndRequiresNew() throws Exception { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status.hasTransaction()).isFalse(); + assertThat(status.isNewTransaction()).isFalse(); + assertThat(status.isNested()).isFalse(); + TransactionTemplate tt2 = new TransactionTemplate(tm); + tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + tt2.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isFalse(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isFalse(); - TransactionTemplate tt2 = new TransactionTemplate(tm); - tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - tt2.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con); - assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con); - } - }); - } + assertThat(status2.hasTransaction()).isTrue(); + assertThat(status2.isNewTransaction()).isTrue(); + assertThat(status2.isNested()).isFalse(); + assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con); + assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con); + }); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -806,7 +718,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testPropagationSupportsAndRequiresNewWithEarlyAccess() throws Exception { + void propagationSupportsAndRequiresNewWithEarlyAccess() throws Exception { Connection con1 = mock(); Connection con2 = mock(); given(ds.getConnection()).willReturn(con1, con2); @@ -816,31 +728,25 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1); + assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1); + assertThat(status.hasTransaction()).isFalse(); + assertThat(status.isNewTransaction()).isFalse(); + assertThat(status.isNested()).isFalse(); + TransactionTemplate tt2 = new TransactionTemplate(tm); + tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + tt2.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1); - assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1); - assertThat(status.hasTransaction()).isFalse(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isFalse(); - TransactionTemplate tt2 = new TransactionTemplate(tm); - tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - tt2.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con2); - assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con2); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - } - }); - assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1); - } + assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con2); + assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con2); + assertThat(status2.hasTransaction()).isTrue(); + assertThat(status2.isNewTransaction()).isTrue(); + assertThat(status2.isNested()).isFalse(); + }); + assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -850,13 +756,15 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionWithIsolationAndReadOnly() throws Exception { + void transactionWithIsolationAndReadOnly() throws Exception { given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_UNCOMMITTED); given(con.getAutoCommit()).willReturn(true); + given(con.isReadOnly()).willReturn(false); - doTestTransactionReadOnly(TransactionDefinition.ISOLATION_REPEATABLE_READ, false); + testTransactionReadOnly(TransactionDefinition.ISOLATION_REPEATABLE_READ, false); InOrder ordered = inOrder(con); + ordered.verify(con).isReadOnly(); ordered.verify(con).setReadOnly(true); ordered.verify(con).getTransactionIsolation(); ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); @@ -871,16 +779,35 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionWithEnforceReadOnly() throws Exception { + void transactionWithDefaultReadOnly() throws Exception { + given(con.getAutoCommit()).willReturn(true); + given(con.isReadOnly()).willReturn(true); + + testTransactionReadOnly(TransactionDefinition.ISOLATION_DEFAULT, false); + + InOrder ordered = inOrder(con); + ordered.verify(con).isReadOnly(); + ordered.verify(con).getAutoCommit(); + ordered.verify(con).setAutoCommit(false); + ordered.verify(con).commit(); + ordered.verify(con).setAutoCommit(true); + ordered.verify(con).close(); + verifyNoMoreInteractions(con); + } + + @Test + void transactionWithEnforceReadOnly() throws Exception { tm.setEnforceReadOnly(true); given(con.getAutoCommit()).willReturn(true); + given(con.isReadOnly()).willReturn(false); Statement stmt = mock(); given(con.createStatement()).willReturn(stmt); - doTestTransactionReadOnly(TransactionDefinition.ISOLATION_DEFAULT, false); + testTransactionReadOnly(TransactionDefinition.ISOLATION_DEFAULT, false); InOrder ordered = inOrder(con, stmt); + ordered.verify(con).isReadOnly(); ordered.verify(con).setReadOnly(true); ordered.verify(con).getAutoCommit(); ordered.verify(con).setAutoCommit(false); @@ -895,14 +822,18 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionWithLazyConnectionDataSourceAndStatement() throws Exception { + void transactionWithLazyConnectionDataSourceAndStatement() throws Exception { LazyConnectionDataSourceProxy dsProxy = new LazyConnectionDataSourceProxy(); dsProxy.setTargetDataSource(ds); dsProxy.setDefaultAutoCommit(true); dsProxy.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + dsProxy.afterPropertiesSet(); tm = createTransactionManager(dsProxy); - doTestTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, true); + try (Connection con = dsProxy.getConnection()) { + assertThat(con.isReadOnly()).isFalse(); + } + testTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, true); InOrder ordered = inOrder(con); ordered.verify(con).setReadOnly(true); @@ -918,27 +849,35 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionWithLazyConnectionDataSourceNoStatement() { + void transactionWithLazyConnectionDataSourceNoStatement() throws Exception { LazyConnectionDataSourceProxy dsProxy = new LazyConnectionDataSourceProxy(); dsProxy.setTargetDataSource(ds); dsProxy.setDefaultAutoCommit(true); dsProxy.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + dsProxy.afterPropertiesSet(); tm = createTransactionManager(dsProxy); - doTestTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, false); + try (Connection con = dsProxy.getConnection()) { + assertThat(con.isReadOnly()).isFalse(); + } + testTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, false); verifyNoMoreInteractions(con); } @Test - void testTransactionWithReadOnlyDataSourceAndStatement() throws Exception { + void transactionWithReadOnlyDataSourceAndStatement() throws Exception { LazyConnectionDataSourceProxy dsProxy = new LazyConnectionDataSourceProxy(); dsProxy.setReadOnlyDataSource(ds); dsProxy.setDefaultAutoCommit(false); dsProxy.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + dsProxy.afterPropertiesSet(); tm = createTransactionManager(dsProxy); - doTestTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, true); + try (Connection con = dsProxy.getConnection()) { + assertThat(con.isReadOnly()).isTrue(); + } + testTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, true); InOrder ordered = inOrder(con); ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); @@ -950,19 +889,23 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionWithReadOnlyDataSourceNoStatement() { + void transactionWithReadOnlyDataSourceNoStatement() throws Exception { LazyConnectionDataSourceProxy dsProxy = new LazyConnectionDataSourceProxy(); dsProxy.setReadOnlyDataSource(ds); dsProxy.setDefaultAutoCommit(false); dsProxy.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + dsProxy.afterPropertiesSet(); tm = createTransactionManager(dsProxy); - doTestTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, false); + try (Connection con = dsProxy.getConnection()) { + assertThat(con.isReadOnly()).isTrue(); + } + testTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, false); verifyNoMoreInteractions(con); } - private void doTestTransactionReadOnly(int isolationLevel, boolean withStatement) { + private void testTransactionReadOnly(int isolationLevel, boolean withStatement) { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); tt.setIsolationLevel(isolationLevel); @@ -971,27 +914,24 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - assertThat(status.getTransactionName()).isEqualTo("my-transaction"); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - assertThat(status.isReadOnly()).isTrue(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isTrue(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); - assertThat(status.isRollbackOnly()).isFalse(); - assertThat(status.isCompleted()).isFalse(); - if (withStatement) { - try { - DataSourceUtils.getConnection(tm.getDataSource()).createStatement(); - } - catch (SQLException ex) { - throw new IllegalStateException(ex); - } + tt.executeWithoutResult(status -> { + // something transactional + assertThat(status.getTransactionName()).isEqualTo("my-transaction"); + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + assertThat(status.isReadOnly()).isTrue(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isTrue(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); + assertThat(status.isRollbackOnly()).isFalse(); + assertThat(status.isCompleted()).isFalse(); + if (withStatement) { + try { + DataSourceUtils.getConnection(tm.getDataSource()).createStatement(); + } + catch (SQLException ex) { + throw new IllegalStateException(ex); } } }); @@ -1012,22 +952,19 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); try { - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - try { - Thread.sleep(1500); - } - catch (InterruptedException ex) { - } - try { - Connection con = DataSourceUtils.getConnection(ds); - PreparedStatement ps = con.prepareStatement("some SQL statement"); - DataSourceUtils.applyTransactionTimeout(ps, ds); - } - catch (SQLException ex) { - throw new DataAccessResourceFailureException("", ex); - } + tt.executeWithoutResult(status -> { + try { + Thread.sleep(1500); + } + catch (InterruptedException ex) { + } + try { + Connection con = DataSourceUtils.getConnection(ds); + PreparedStatement ps2 = con.prepareStatement("some SQL statement"); + DataSourceUtils.applyTransactionTimeout(ps2, ds); + } + catch (SQLException ex) { + throw new DataAccessResourceFailureException("", ex); } }); if (timeout <= 1) { @@ -1058,29 +995,26 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionAwareDataSourceProxy() throws Exception { + void transactionAwareDataSourceProxy() throws Exception { given(con.getAutoCommit()).willReturn(true); given(con.getWarnings()).willThrow(new SQLException()); TransactionTemplate tt = new TransactionTemplate(tm); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); - TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); - try { - Connection tCon = dsProxy.getConnection(); - tCon.getWarnings(); - tCon.clearWarnings(); - assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con); - // should be ignored - tCon.close(); - } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); - } + tt.executeWithoutResult(status -> { + // something transactional + assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); + TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); + try { + Connection tCon = dsProxy.getConnection(); + tCon.getWarnings(); + tCon.clearWarnings(); + assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con); + // should be ignored + tCon.close(); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); } }); @@ -1093,30 +1027,27 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionAwareDataSourceProxyWithLazyFalse() throws Exception { + void transactionAwareDataSourceProxyWithLazyFalse() throws Exception { given(con.getAutoCommit()).willReturn(true); given(con.getWarnings()).willThrow(new SQLException()); TransactionTemplate tt = new TransactionTemplate(tm); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); - TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); - dsProxy.setLazyTransactionalConnections(false); - try { - Connection tCon = dsProxy.getConnection(); - assertThatExceptionOfType(SQLException.class).isThrownBy(tCon::getWarnings); - tCon.clearWarnings(); - assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con); - // should be ignored - tCon.close(); - } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); - } + tt.executeWithoutResult(status -> { + // something transactional + assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); + TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); + dsProxy.setLazyTransactionalConnections(false); + try { + Connection tCon = dsProxy.getConnection(); + assertThatExceptionOfType(SQLException.class).isThrownBy(tCon::getWarnings); + tCon.clearWarnings(); + assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con); + // should be ignored + tCon.close(); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); } }); @@ -1129,7 +1060,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionAwareDataSourceProxyWithEarlyConnection() throws Exception { + void transactionAwareDataSourceProxyWithEarlyConnection() throws Exception { given(ds.getConnection()).willReturn(mock(Connection.class), con); given(con.getAutoCommit()).willReturn(true); given(con.getWarnings()).willThrow(new SQLException()); @@ -1140,18 +1071,15 @@ public class DataSourceTransactionManagerTests { TransactionTemplate tt = new TransactionTemplate(tm); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); - try { - // should close the early Connection obtained before the transaction - tCon.close(); - } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); - } + tt.executeWithoutResult(status -> { + // something transactional + assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); + try { + // should close the early Connection obtained before the transaction + tCon.close(); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); } }); @@ -1165,19 +1093,29 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionAwareDataSourceProxyWithSuspension() throws Exception { + void transactionAwareDataSourceProxyWithSuspension() throws Exception { given(con.getAutoCommit()).willReturn(true); TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { + tt.executeWithoutResult(status -> { + // something transactional + assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); + TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); + try { + assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); + // should be ignored + dsProxy.getConnection().close(); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); + } + + tt.executeWithoutResult(status2 -> { // something transactional assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); - TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); try { assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); // should be ignored @@ -1186,31 +1124,15 @@ public class DataSourceTransactionManagerTests { catch (SQLException ex) { throw new UncategorizedSQLException("", "", ex); } + }); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); - try { - assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); - // should be ignored - dsProxy.getConnection().close(); - } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); - } - } - }); - - try { - assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); - // should be ignored - dsProxy.getConnection().close(); - } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); - } + try { + assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); + // should be ignored + dsProxy.getConnection().close(); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); } }); @@ -1223,20 +1145,30 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionAwareDataSourceProxyWithSuspensionAndReobtaining() throws Exception { + void transactionAwareDataSourceProxyWithSuspensionAndReobtaining() throws Exception { given(con.getAutoCommit()).willReturn(true); TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { + tt.executeWithoutResult(status -> { + // something transactional + assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); + TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); + dsProxy.setReobtainTransactionalConnections(true); + try { + assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); + // should be ignored + dsProxy.getConnection().close(); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); + } + + tt.executeWithoutResult(status2 -> { // something transactional assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); - TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); - dsProxy.setReobtainTransactionalConnections(true); try { assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); // should be ignored @@ -1245,31 +1177,15 @@ public class DataSourceTransactionManagerTests { catch (SQLException ex) { throw new UncategorizedSQLException("", "", ex); } + }); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); - try { - assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); - // should be ignored - dsProxy.getConnection().close(); - } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); - } - } - }); - - try { - assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); - // should be ignored - dsProxy.getConnection().close(); - } - catch (SQLException ex) { - throw new UncategorizedSQLException("", "", ex); - } + try { + assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con); + // should be ignored + dsProxy.getConnection().close(); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); } }); @@ -1285,16 +1201,13 @@ public class DataSourceTransactionManagerTests { * Test behavior if the first operation on a connection (getAutoCommit) throws SQLException. */ @Test - void testTransactionWithExceptionOnBegin() throws Exception { + void transactionWithExceptionOnBegin() throws Exception { willThrow(new SQLException("Cannot begin")).given(con).getAutoCommit(); TransactionTemplate tt = new TransactionTemplate(tm); assertThatExceptionOfType(CannotCreateTransactionException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - } + tt.executeWithoutResult(status -> { + // something transactional })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1302,16 +1215,13 @@ public class DataSourceTransactionManagerTests { } @Test - protected void testTransactionWithExceptionOnCommit() throws Exception { + protected void transactionWithExceptionOnCommit() throws Exception { willThrow(new SQLException("Cannot commit")).given(con).commit(); TransactionTemplate tt = new TransactionTemplate(tm); assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - } + tt.executeWithoutResult(status -> { + // something transactional })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1319,17 +1229,14 @@ public class DataSourceTransactionManagerTests { } @Test - protected void testTransactionWithExceptionOnCommitAndRollbackOnCommitFailure() throws Exception { + protected void transactionWithExceptionOnCommitAndRollbackOnCommitFailure() throws Exception { willThrow(new SQLException("Cannot commit")).given(con).commit(); tm.setRollbackOnCommitFailure(true); TransactionTemplate tt = new TransactionTemplate(tm); assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - } + tt.executeWithoutResult(status -> { + // something transactional })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1338,26 +1245,23 @@ public class DataSourceTransactionManagerTests { } @Test - protected void testTransactionWithExceptionOnRollback() throws Exception { + protected void transactionWithExceptionOnRollback() throws Exception { given(con.getAutoCommit()).willReturn(true); willThrow(new SQLException("Cannot rollback")).given(con).rollback(); TransactionTemplate tt = new TransactionTemplate(tm); assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.getTransactionName()).isEmpty(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - assertThat(status.isReadOnly()).isFalse(); - assertThat(status.isRollbackOnly()).isFalse(); - status.setRollbackOnly(); - assertThat(status.isRollbackOnly()).isTrue(); - assertThat(status.isCompleted()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(status.getTransactionName()).isEmpty(); + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + assertThat(status.isReadOnly()).isFalse(); + assertThat(status.isRollbackOnly()).isFalse(); + status.setRollbackOnly(); + assertThat(status.isRollbackOnly()).isTrue(); + assertThat(status.isCompleted()).isFalse(); })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1369,69 +1273,60 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionWithPropagationSupports() { + void transactionWithPropagationSupports() { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); + assertThat(status.isNewTransaction()).isFalse(); + assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); } @Test - void testTransactionWithPropagationNotSupported() { + void transactionWithPropagationNotSupported() { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - assertThat(status.isNewTransaction()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); + assertThat(status.isNewTransaction()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); } @Test - void testTransactionWithPropagationNever() { + void transactionWithPropagationNever() { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); - assertThat(status.isNewTransaction()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); + assertThat(status.isNewTransaction()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); } @Test - void testExistingTransactionWithPropagationNested() throws Exception { - doTestExistingTransactionWithPropagationNested(1); + void existingTransactionWithPropagationNested() throws Exception { + testExistingTransactionWithPropagationNested(1); } @Test - void testExistingTransactionWithPropagationNestedTwice() throws Exception { - doTestExistingTransactionWithPropagationNested(2); + void existingTransactionWithPropagationNestedTwice() throws Exception { + testExistingTransactionWithPropagationNested(2); } - private void doTestExistingTransactionWithPropagationNested(int count) throws Exception { + private void testExistingTransactionWithPropagationNested(int count) throws Exception { DatabaseMetaData md = mock(); Savepoint sp = mock(); @@ -1446,36 +1341,30 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - TestSavepointSynchronization synch = new TestSavepointSynchronization(); - TransactionSynchronizationManager.registerSynchronization(synch); - for (int i = 0; i < count; i++) { - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isTrue(); - assertThat(status.hasSavepoint()).isTrue(); - assertThat(synch.savepointCalled).isTrue(); - } - }); - assertThat(synch.savepointRollbackCalled).isFalse(); - synch.savepointCalled = false; - } - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); + tt.executeWithoutResult(status -> { + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + TestSavepointSynchronization synch = new TestSavepointSynchronization(); + TransactionSynchronizationManager.registerSynchronization(synch); + for (int i = 0; i < count; i++) { + tt.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.hasTransaction()).isTrue(); + assertThat(status2.isNewTransaction()).isFalse(); + assertThat(status2.isNested()).isTrue(); + assertThat(status2.hasSavepoint()).isTrue(); + assertThat(synch.savepointCalled).isTrue(); + }); + assertThat(synch.savepointRollbackCalled).isFalse(); + synch.savepointCalled = false; } + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1485,7 +1374,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testExistingTransactionWithPropagationNestedAndRollback() throws Exception { + void existingTransactionWithPropagationNestedAndRollback() throws Exception { DatabaseMetaData md = mock(); Savepoint sp = mock(); @@ -1498,35 +1387,29 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - TestSavepointSynchronization synch = new TestSavepointSynchronization(); - TransactionSynchronizationManager.registerSynchronization(synch); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isTrue(); - assertThat(status.hasSavepoint()).isTrue(); - assertThat(synch.savepointCalled).isTrue(); - assertThat(synch.savepointRollbackCalled).isFalse(); - status.setRollbackOnly(); - } - }); - assertThat(synch.savepointRollbackCalled).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + TestSavepointSynchronization synch = new TestSavepointSynchronization(); + TransactionSynchronizationManager.registerSynchronization(synch); + tt.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.hasTransaction()).isTrue(); + assertThat(status2.isNewTransaction()).isFalse(); + assertThat(status2.isNested()).isTrue(); + assertThat(status2.hasSavepoint()).isTrue(); + assertThat(synch.savepointCalled).isTrue(); + assertThat(synch.savepointRollbackCalled).isFalse(); + status2.setRollbackOnly(); + }); + assertThat(synch.savepointRollbackCalled).isTrue(); + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1537,7 +1420,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testExistingTransactionWithPropagationNestedAndRequiredRollback() throws Exception { + void existingTransactionWithPropagationNestedAndRequiredRollback() throws Exception { DatabaseMetaData md = mock(); Savepoint sp = mock(); @@ -1550,48 +1433,39 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - TestSavepointSynchronization synch = new TestSavepointSynchronization(); - TransactionSynchronizationManager.registerSynchronization(synch); - assertThatIllegalStateException().isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isTrue(); - assertThat(status.hasSavepoint()).isTrue(); - assertThat(synch.savepointCalled).isTrue(); - assertThat(synch.savepointRollbackCalled).isFalse(); - TransactionTemplate ntt = new TransactionTemplate(tm); - ntt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - throw new IllegalStateException(); - } - }); - } - })); - assertThat(synch.savepointRollbackCalled).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + TestSavepointSynchronization synch = new TestSavepointSynchronization(); + TransactionSynchronizationManager.registerSynchronization(synch); + assertThatIllegalStateException().isThrownBy(() -> + tt.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.hasTransaction()).isTrue(); + assertThat(status2.isNewTransaction()).isFalse(); + assertThat(status2.isNested()).isTrue(); + assertThat(status2.hasSavepoint()).isTrue(); + assertThat(synch.savepointCalled).isTrue(); + assertThat(synch.savepointRollbackCalled).isFalse(); + TransactionTemplate ntt = new TransactionTemplate(tm); + ntt.executeWithoutResult(status3 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status3.hasTransaction()).isTrue(); + assertThat(status3.isNewTransaction()).isFalse(); + assertThat(status3.isNested()).isFalse(); + assertThat(status3.hasSavepoint()).isFalse(); + throw new IllegalStateException(); + }); + })); + assertThat(synch.savepointRollbackCalled).isTrue(); + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1602,7 +1476,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testExistingTransactionWithPropagationNestedAndRequiredRollbackOnly() throws Exception { + void existingTransactionWithPropagationNestedAndRequiredRollbackOnly() throws Exception { DatabaseMetaData md = mock(); Savepoint sp = mock(); @@ -1615,48 +1489,39 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - TestSavepointSynchronization synch = new TestSavepointSynchronization(); - TransactionSynchronizationManager.registerSynchronization(synch); - assertThatExceptionOfType(UnexpectedRollbackException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isTrue(); - assertThat(status.hasSavepoint()).isTrue(); - assertThat(synch.savepointCalled).isTrue(); - assertThat(synch.savepointRollbackCalled).isFalse(); - TransactionTemplate ntt = new TransactionTemplate(tm); - ntt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isFalse(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - status.setRollbackOnly(); - } - }); - } - })); + tt.executeWithoutResult(status1 -> { + assertThat(status1.hasTransaction()).isTrue(); + assertThat(status1.isNewTransaction()).isTrue(); + assertThat(status1.isNested()).isFalse(); + assertThat(status1.hasSavepoint()).isFalse(); + TestSavepointSynchronization synch = new TestSavepointSynchronization(); + TransactionSynchronizationManager.registerSynchronization(synch); + assertThatExceptionOfType(UnexpectedRollbackException.class).isThrownBy(() -> + tt.executeWithoutResult(status2 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status2.hasTransaction()).isTrue(); + assertThat(status2.isNewTransaction()).isFalse(); + assertThat(status2.isNested()).isTrue(); + assertThat(status2.hasSavepoint()).isTrue(); + assertThat(synch.savepointCalled).isTrue(); + assertThat(synch.savepointRollbackCalled).isFalse(); + TransactionTemplate ntt = new TransactionTemplate(tm); + ntt.executeWithoutResult(status3 -> { + assertThat(TransactionSynchronizationManager.hasResource(ds)).isTrue(); + assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); + assertThat(status3.hasTransaction()).isTrue(); + assertThat(status3.isNewTransaction()).isFalse(); + assertThat(status3.isNested()).isFalse(); + assertThat(status3.hasSavepoint()).isFalse(); + status3.setRollbackOnly(); + }); + })); assertThat(synch.savepointRollbackCalled).isTrue(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - } + assertThat(status1.hasTransaction()).isTrue(); + assertThat(status1.isNewTransaction()).isTrue(); + assertThat(status1.isNested()).isFalse(); + assertThat(status1.hasSavepoint()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1667,7 +1532,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testExistingTransactionWithManualSavepoint() throws Exception { + void existingTransactionWithManualSavepoint() throws Exception { DatabaseMetaData md = mock(); Savepoint sp = mock(); @@ -1680,20 +1545,17 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - TestSavepointSynchronization synch = new TestSavepointSynchronization(); - TransactionSynchronizationManager.registerSynchronization(synch); - Object savepoint = status.createSavepoint(); - assertThat(synch.savepointCalled).isTrue(); - status.releaseSavepoint(savepoint); - assertThat(synch.savepointRollbackCalled).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + TestSavepointSynchronization synch = new TestSavepointSynchronization(); + TransactionSynchronizationManager.registerSynchronization(synch); + Object savepoint = status.createSavepoint(); + assertThat(synch.savepointCalled).isTrue(); + status.releaseSavepoint(savepoint); + assertThat(synch.savepointRollbackCalled).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1704,7 +1566,7 @@ public class DataSourceTransactionManagerTests { } @Test - void testExistingTransactionWithManualSavepointAndRollback() throws Exception { + void existingTransactionWithManualSavepointAndRollback() throws Exception { DatabaseMetaData md = mock(); Savepoint sp = mock(); @@ -1717,21 +1579,18 @@ public class DataSourceTransactionManagerTests { assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - TestSavepointSynchronization synch = new TestSavepointSynchronization(); - TransactionSynchronizationManager.registerSynchronization(synch); - Object savepoint = status.createSavepoint(); - assertThat(synch.savepointCalled).isTrue(); - assertThat(synch.savepointRollbackCalled).isFalse(); - status.rollbackToSavepoint(savepoint); - assertThat(synch.savepointRollbackCalled).isTrue(); - } + tt.executeWithoutResult(status -> { + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + TestSavepointSynchronization synch = new TestSavepointSynchronization(); + TransactionSynchronizationManager.registerSynchronization(synch); + Object savepoint = status.createSavepoint(); + assertThat(synch.savepointCalled).isTrue(); + assertThat(synch.savepointRollbackCalled).isFalse(); + status.rollbackToSavepoint(savepoint); + assertThat(synch.savepointRollbackCalled).isTrue(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1741,24 +1600,21 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionWithPropagationNested() throws Exception { + void transactionWithPropagationNested() throws Exception { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.getTransactionName()).isEmpty(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - assertThat(status.isReadOnly()).isFalse(); - assertThat(status.isRollbackOnly()).isFalse(); - assertThat(status.isCompleted()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(status.getTransactionName()).isEmpty(); + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + assertThat(status.isReadOnly()).isFalse(); + assertThat(status.isRollbackOnly()).isFalse(); + assertThat(status.isCompleted()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -1767,26 +1623,23 @@ public class DataSourceTransactionManagerTests { } @Test - void testTransactionWithPropagationNestedAndRollback() throws Exception { + void transactionWithPropagationNestedAndRollback() throws Exception { TransactionTemplate tt = new TransactionTemplate(tm); tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.getTransactionName()).isEmpty(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - assertThat(status.isReadOnly()).isFalse(); - assertThat(status.isRollbackOnly()).isFalse(); - status.setRollbackOnly(); - assertThat(status.isRollbackOnly()).isTrue(); - assertThat(status.isCompleted()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(status.getTransactionName()).isEmpty(); + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + assertThat(status.isReadOnly()).isFalse(); + assertThat(status.isRollbackOnly()).isFalse(); + status.setRollbackOnly(); + assertThat(status.isRollbackOnly()).isTrue(); + assertThat(status.isCompleted()).isFalse(); }); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java index 7dc516e875b..7405f622f64 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java @@ -25,9 +25,7 @@ import org.mockito.InOrder; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.jdbc.datasource.DataSourceTransactionManagerTests; -import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionSystemException; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; @@ -53,36 +51,30 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { @Override @Test - protected void testTransactionWithExceptionOnCommit() throws Exception { + protected void transactionWithExceptionOnCommit() throws Exception { willThrow(new SQLException("Cannot commit")).given(con).commit(); TransactionTemplate tt = new TransactionTemplate(tm); // plain TransactionSystemException assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { + tt.executeWithoutResult(status -> { // something transactional - } - })); + })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); verify(con).close(); } @Test - void testTransactionWithDataAccessExceptionOnCommit() throws Exception { + void transactionWithDataAccessExceptionOnCommit() throws Exception { willThrow(new SQLException("Cannot commit")).given(con).commit(); ((JdbcTransactionManager) tm).setExceptionTranslator((task, sql, ex) -> new ConcurrencyFailureException(task)); TransactionTemplate tt = new TransactionTemplate(tm); // specific ConcurrencyFailureException assertThatExceptionOfType(ConcurrencyFailureException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - } + tt.executeWithoutResult(status -> { + // something transactional })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -90,17 +82,14 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { } @Test - void testTransactionWithDataAccessExceptionOnCommitFromLazyExceptionTranslator() throws Exception { + void transactionWithDataAccessExceptionOnCommitFromLazyExceptionTranslator() throws Exception { willThrow(new SQLException("Cannot commit", "40")).given(con).commit(); TransactionTemplate tt = new TransactionTemplate(tm); // specific ConcurrencyFailureException assertThatExceptionOfType(ConcurrencyFailureException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // something transactional - } + tt.executeWithoutResult(status -> { + // something transactional })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); @@ -109,7 +98,7 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { @Override @Test - protected void testTransactionWithExceptionOnCommitAndRollbackOnCommitFailure() throws Exception { + protected void transactionWithExceptionOnCommitAndRollbackOnCommitFailure() throws Exception { willThrow(new SQLException("Cannot commit")).given(con).commit(); tm.setRollbackOnCommitFailure(true); @@ -117,12 +106,9 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { // plain TransactionSystemException assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { + tt.executeWithoutResult(status -> { // something transactional - } - })); + })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); verify(con).rollback(); @@ -131,16 +117,14 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { @Override @Test - protected void testTransactionWithExceptionOnRollback() throws Exception { + protected void transactionWithExceptionOnRollback() throws Exception { given(con.getAutoCommit()).willReturn(true); willThrow(new SQLException("Cannot rollback")).given(con).rollback(); TransactionTemplate tt = new TransactionTemplate(tm); // plain TransactionSystemException assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { + tt.executeWithoutResult(status -> { assertThat(status.getTransactionName()).isEmpty(); assertThat(status.hasTransaction()).isTrue(); assertThat(status.isNewTransaction()).isTrue(); @@ -151,8 +135,7 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { status.setRollbackOnly(); assertThat(status.isRollbackOnly()).isTrue(); assertThat(status.isCompleted()).isFalse(); - } - })); + })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); InOrder ordered = inOrder(con); @@ -163,7 +146,7 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { } @Test - void testTransactionWithDataAccessExceptionOnRollback() throws Exception { + void transactionWithDataAccessExceptionOnRollback() throws Exception { given(con.getAutoCommit()).willReturn(true); willThrow(new SQLException("Cannot rollback")).given(con).rollback(); ((JdbcTransactionManager) tm).setExceptionTranslator((task, sql, ex) -> new ConcurrencyFailureException(task)); @@ -171,12 +154,7 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { // specific ConcurrencyFailureException assertThatExceptionOfType(ConcurrencyFailureException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - status.setRollbackOnly(); - } - })); + tt.executeWithoutResult(status -> status.setRollbackOnly())); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse(); InOrder ordered = inOrder(con); @@ -187,27 +165,24 @@ class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests { } @Test - void testTransactionWithDataAccessExceptionOnRollbackFromLazyExceptionTranslator() throws Exception { + void transactionWithDataAccessExceptionOnRollbackFromLazyExceptionTranslator() throws Exception { given(con.getAutoCommit()).willReturn(true); willThrow(new SQLException("Cannot rollback", "40")).given(con).rollback(); TransactionTemplate tt = new TransactionTemplate(tm); // specific ConcurrencyFailureException assertThatExceptionOfType(ConcurrencyFailureException.class).isThrownBy(() -> - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException { - assertThat(status.getTransactionName()).isEmpty(); - assertThat(status.hasTransaction()).isTrue(); - assertThat(status.isNewTransaction()).isTrue(); - assertThat(status.isNested()).isFalse(); - assertThat(status.hasSavepoint()).isFalse(); - assertThat(status.isReadOnly()).isFalse(); - assertThat(status.isRollbackOnly()).isFalse(); - status.setRollbackOnly(); - assertThat(status.isRollbackOnly()).isTrue(); - assertThat(status.isCompleted()).isFalse(); - } + tt.executeWithoutResult(status -> { + assertThat(status.getTransactionName()).isEmpty(); + assertThat(status.hasTransaction()).isTrue(); + assertThat(status.isNewTransaction()).isTrue(); + assertThat(status.isNested()).isFalse(); + assertThat(status.hasSavepoint()).isFalse(); + assertThat(status.isReadOnly()).isFalse(); + assertThat(status.isRollbackOnly()).isFalse(); + status.setRollbackOnly(); + assertThat(status.isRollbackOnly()).isTrue(); + assertThat(status.isCompleted()).isFalse(); })); assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();