From 2880e6fba539fba84ff6acff9b211216a6728ac7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Sep 2023 17:36:00 +0200 Subject: [PATCH 1/4] Consistently release savepoint after nested transaction Closes gh-31133 --- .../connection/R2dbcTransactionManager.java | 73 +++++---- .../R2dbcTransactionManagerUnitTests.java | 140 +++++++++++++----- 2 files changed, 145 insertions(+), 68 deletions(-) diff --git a/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/R2dbcTransactionManager.java b/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/R2dbcTransactionManager.java index 325eb5524ce..f3edbf16b9a 100644 --- a/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/R2dbcTransactionManager.java +++ b/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/R2dbcTransactionManager.java @@ -337,36 +337,36 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager return Mono.defer(() -> { ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction; + if (txObject.hasSavepoint()) { + // Just release the savepoint, keeping the transactional connection. + return txObject.releaseSavepoint(); + } + // Remove the connection holder from the context, if exposed. if (txObject.isNewConnectionHolder()) { synchronizationManager.unbindResource(obtainConnectionFactory()); } // Reset connection. - Connection con = txObject.getConnectionHolder().getConnection(); - - Mono afterCleanup = Mono.empty(); - - Mono releaseConnectionStep = Mono.defer(() -> { - try { - if (txObject.isNewConnectionHolder()) { - if (logger.isDebugEnabled()) { - logger.debug("Releasing R2DBC Connection [" + con + "] after transaction"); - } - Mono releaseMono = ConnectionFactoryUtils.releaseConnection(con, obtainConnectionFactory()); - if (logger.isDebugEnabled()) { - releaseMono = releaseMono.doOnError( - ex -> logger.debug(String.format("Error ignored during cleanup: %s", ex))); - } - return releaseMono.onErrorComplete(); + try { + if (txObject.isNewConnectionHolder()) { + Connection con = txObject.getConnectionHolder().getConnection(); + if (logger.isDebugEnabled()) { + logger.debug("Releasing R2DBC Connection [" + con + "] after transaction"); } + Mono releaseMono = ConnectionFactoryUtils.releaseConnection(con, obtainConnectionFactory()); + if (logger.isDebugEnabled()) { + releaseMono = releaseMono.doOnError( + ex -> logger.debug(String.format("Error ignored during cleanup: %s", ex))); + } + return releaseMono.onErrorComplete(); } - finally { - txObject.getConnectionHolder().clear(); - } - return Mono.empty(); - }); - return afterCleanup.then(releaseConnectionStep); + } + finally { + txObject.getConnectionHolder().clear(); + } + + return Mono.empty(); }); } @@ -511,23 +511,36 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager return (this.connectionHolder != null && this.connectionHolder.isTransactionActive()); } + public boolean hasSavepoint() { + return (this.savepointName != null); + } + public Mono createSavepoint() { ConnectionHolder holder = getConnectionHolder(); - this.savepointName = holder.nextSavepoint(); - return Mono.from(holder.getConnection().createSavepoint(this.savepointName)); + String currentSavepoint = holder.nextSavepoint(); + this.savepointName = currentSavepoint; + return Mono.from(holder.getConnection().createSavepoint(currentSavepoint)); + } + + public Mono releaseSavepoint() { + String currentSavepoint = this.savepointName; + if (currentSavepoint == null) { + return Mono.empty(); + } + this.savepointName = null; + return Mono.from(getConnectionHolder().getConnection().releaseSavepoint(currentSavepoint)); } public Mono commit() { - Connection connection = getConnectionHolder().getConnection(); - return (this.savepointName != null ? - Mono.from(connection.releaseSavepoint(this.savepointName)) : - Mono.from(connection.commitTransaction())); + return (hasSavepoint() ? Mono.empty() : + Mono.from(getConnectionHolder().getConnection().commitTransaction())); } public Mono rollback() { Connection connection = getConnectionHolder().getConnection(); - return (this.savepointName != null ? - Mono.from(connection.rollbackTransactionToSavepoint(this.savepointName)) : + String currentSavepoint = this.savepointName; + return (currentSavepoint != null ? + Mono.from(connection.rollbackTransactionToSavepoint(currentSavepoint)) : Mono.from(connection.rollbackTransaction())); } diff --git a/spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java b/spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java index 99cf809646a..05cc75cfc04 100644 --- a/spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java +++ b/spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java @@ -27,6 +27,8 @@ import io.r2dbc.spi.Statement; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -44,6 +46,7 @@ import static org.assertj.core.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.inOrder; import static org.mockito.BDDMockito.mock; import static org.mockito.BDDMockito.never; import static org.mockito.BDDMockito.reset; @@ -365,53 +368,110 @@ class R2dbcTransactionManagerUnitTests { @Test void testPropagationNestedWithExistingTransaction() { - when(connectionMock.createSavepoint("SAVEPOINT_1")).thenReturn(Mono.empty()); - when(connectionMock.releaseSavepoint("SAVEPOINT_1")).thenReturn(Mono.empty()); + when(connectionMock.createSavepoint(anyString())).thenReturn(Mono.empty()); + when(connectionMock.rollbackTransactionToSavepoint(anyString())).thenReturn(Mono.empty()); + when(connectionMock.releaseSavepoint(anyString())).thenReturn(Mono.empty()); when(connectionMock.commitTransaction()).thenReturn(Mono.empty()); DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); TransactionalOperator operator = TransactionalOperator.create(tm, definition); - operator.execute(tx1 -> { - assertThat(tx1.isNewTransaction()).isTrue(); - definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED); - return operator.execute(tx2 -> { - assertThat(tx2.isNewTransaction()).isTrue(); - return Mono.empty(); - }); - }).as(StepVerifier::create).verifyComplete(); - - verify(connectionMock).createSavepoint("SAVEPOINT_1"); - verify(connectionMock).releaseSavepoint("SAVEPOINT_1"); - verify(connectionMock).commitTransaction(); - verify(connectionMock).close(); - } - - @Test - void testPropagationNestedWithExistingTransactionAndRollback() { - when(connectionMock.createSavepoint("SAVEPOINT_1")).thenReturn(Mono.empty()); - when(connectionMock.rollbackTransactionToSavepoint("SAVEPOINT_1")).thenReturn(Mono.empty()); - when(connectionMock.commitTransaction()).thenReturn(Mono.empty()); - - DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); - definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - - TransactionalOperator operator = TransactionalOperator.create(tm, definition); - operator.execute(tx1 -> { - assertThat(tx1.isNewTransaction()).isTrue(); + operator.execute(tx -> { + assertThat(tx.isNewTransaction()).isTrue(); definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED); - return operator.execute(tx2 -> { - assertThat(tx2.isNewTransaction()).isTrue(); - tx2.setRollbackOnly(); - return Mono.empty(); - }); + return Flux.concat( + TransactionalOperator.create(tm, definition).execute(ntx1 -> { + assertThat(ntx1.isNewTransaction()).as("ntx1.isNewTransaction()").isTrue(); + assertThat(ntx1.isRollbackOnly()).as("ntx1.isRollbackOnly()").isFalse(); + return Mono.empty(); + }), + TransactionalOperator.create(tm, definition).execute(ntx2 -> { + assertThat(ntx2.isNewTransaction()).as("ntx2.isNewTransaction()").isTrue(); + assertThat(ntx2.isRollbackOnly()).as("ntx2.isRollbackOnly()").isFalse(); + ntx2.setRollbackOnly(); + assertThat(ntx2.isRollbackOnly()).isTrue(); + return Mono.empty(); + }), + TransactionalOperator.create(tm, definition).execute(ntx3 -> { + assertThat(ntx3.isNewTransaction()).as("ntx3.isNewTransaction()").isTrue(); + assertThat(ntx3.isRollbackOnly()).as("ntx3.isRollbackOnly()").isFalse(); + return Mono.empty(); + }), + TransactionalOperator.create(tm, definition).execute(ntx4 -> { + assertThat(ntx4.isNewTransaction()).as("ntx4.isNewTransaction()").isTrue(); + assertThat(ntx4.isRollbackOnly()).as("ntx4.isRollbackOnly()").isFalse(); + ntx4.setRollbackOnly(); + assertThat(ntx4.isRollbackOnly()).isTrue(); + return Flux.concat( + TransactionalOperator.create(tm, definition).execute(ntx4n1 -> { + assertThat(ntx4n1.isNewTransaction()).as("ntx4n1.isNewTransaction()").isTrue(); + assertThat(ntx4n1.isRollbackOnly()).as("ntx4n1.isRollbackOnly()").isFalse(); + return Mono.empty(); + }), + TransactionalOperator.create(tm, definition).execute(ntx4n2 -> { + assertThat(ntx4n2.isNewTransaction()).as("ntx4n2.isNewTransaction()").isTrue(); + assertThat(ntx4n2.isRollbackOnly()).as("ntx4n2.isRollbackOnly()").isFalse(); + ntx4n2.setRollbackOnly(); + assertThat(ntx4n2.isRollbackOnly()).isTrue(); + return Mono.empty(); + }) + ); + }), + TransactionalOperator.create(tm, definition).execute(ntx5 -> { + assertThat(ntx5.isNewTransaction()).as("ntx5.isNewTransaction()").isTrue(); + assertThat(ntx5.isRollbackOnly()).as("ntx5.isRollbackOnly()").isFalse(); + ntx5.setRollbackOnly(); + assertThat(ntx5.isRollbackOnly()).isTrue(); + return Flux.concat( + TransactionalOperator.create(tm, definition).execute(ntx5n1 -> { + assertThat(ntx5n1.isNewTransaction()).as("ntx5n1.isNewTransaction()").isTrue(); + assertThat(ntx5n1.isRollbackOnly()).as("ntx5n1.isRollbackOnly()").isFalse(); + return Mono.empty(); + }), + TransactionalOperator.create(tm, definition).execute(ntx5n2 -> { + assertThat(ntx5n2.isNewTransaction()).as("ntx5n2.isNewTransaction()").isTrue(); + assertThat(ntx5n2.isRollbackOnly()).as("ntx5n2.isRollbackOnly()").isFalse(); + ntx5n2.setRollbackOnly(); + assertThat(ntx5n2.isRollbackOnly()).isTrue(); + return Mono.empty(); + }) + ); + }) + ); }).as(StepVerifier::create).verifyComplete(); - verify(connectionMock).createSavepoint("SAVEPOINT_1"); - verify(connectionMock).rollbackTransactionToSavepoint("SAVEPOINT_1"); - verify(connectionMock).commitTransaction(); - verify(connectionMock).close(); + InOrder inOrder = inOrder(connectionMock); + // ntx1 + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_1"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_1"); + // ntx2 + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_2"); + inOrder.verify(connectionMock).rollbackTransactionToSavepoint("SAVEPOINT_2"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_2"); + // ntx3 + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_3"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_3"); + // ntx4 + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_4"); + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_5"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_5"); + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_6"); + inOrder.verify(connectionMock).rollbackTransactionToSavepoint("SAVEPOINT_6"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_6"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_4"); + // ntx5 + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_7"); + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_8"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_8"); + inOrder.verify(connectionMock).createSavepoint("SAVEPOINT_9"); + inOrder.verify(connectionMock).rollbackTransactionToSavepoint("SAVEPOINT_9"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_9"); + inOrder.verify(connectionMock).rollbackTransactionToSavepoint("SAVEPOINT_7"); + inOrder.verify(connectionMock).releaseSavepoint("SAVEPOINT_7"); + // tx + inOrder.verify(connectionMock).commitTransaction(); + inOrder.verify(connectionMock).close(); } @Test @@ -452,7 +512,9 @@ class R2dbcTransactionManagerUnitTests { TransactionalOperator inner = TransactionalOperator.create(tm, innerDef); return inner.execute(tx2 -> { assertThat(tx2.isNewTransaction()).isTrue(); + assertThat(tx2.isRollbackOnly()).isFalse(); tx2.setRollbackOnly(); + assertThat(tx2.isRollbackOnly()).isTrue(); return Mono.empty(); }); }).as(StepVerifier::create).verifyComplete(); @@ -499,7 +561,9 @@ class R2dbcTransactionManagerUnitTests { TransactionalOperator inner = TransactionalOperator.create(tm, innerDef); return inner.execute(tx2 -> { assertThat(tx2.isNewTransaction()).isTrue(); + assertThat(tx2.isRollbackOnly()).isFalse(); tx2.setRollbackOnly(); + assertThat(tx2.isRollbackOnly()).isTrue(); return Mono.empty(); }); }).as(StepVerifier::create).verifyComplete(); From 268043e9c9013b41c44861562bacf09b2f5e3085 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Sep 2023 17:36:07 +0200 Subject: [PATCH 2/4] Align abstract method signatures with original Commons Logging API Closes gh-31166 --- .../apache/commons/logging/LogFactory.java | 40 +++++++++++-------- .../commons/logging/LogFactoryService.java | 4 ++ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/spring-jcl/src/main/java/org/apache/commons/logging/LogFactory.java b/spring-jcl/src/main/java/org/apache/commons/logging/LogFactory.java index e67eb5a4342..34426cc33f4 100644 --- a/spring-jcl/src/main/java/org/apache/commons/logging/LogFactory.java +++ b/spring-jcl/src/main/java/org/apache/commons/logging/LogFactory.java @@ -77,7 +77,25 @@ public abstract class LogFactory { */ @Deprecated public static LogFactory getFactory() { - return new LogFactory() {}; + return new LogFactory() { + @Override + public Object getAttribute(String name) { + return null; + } + @Override + public String[] getAttributeNames() { + return new String[0]; + } + @Override + public void removeAttribute(String name) { + } + @Override + public void setAttribute(String name, Object value) { + } + @Override + public void release() { + } + }; } /** @@ -106,29 +124,19 @@ public abstract class LogFactory { // Just in case some code happens to call uncommon Commons Logging methods... @Deprecated - public Object getAttribute(String name) { - return null; - } + public abstract Object getAttribute(String name); @Deprecated - public String[] getAttributeNames() { - return new String[0]; - } + public abstract String[] getAttributeNames(); @Deprecated - public void removeAttribute(String name) { - // do nothing - } + public abstract void removeAttribute(String name); @Deprecated - public void setAttribute(String name, Object value) { - // do nothing - } + public abstract void setAttribute(String name, Object value); @Deprecated - public void release() { - // do nothing - } + public abstract void release(); @Deprecated public static void release(ClassLoader classLoader) { diff --git a/spring-jcl/src/main/java/org/apache/commons/logging/LogFactoryService.java b/spring-jcl/src/main/java/org/apache/commons/logging/LogFactoryService.java index 1e961e68f62..355d4a30bce 100644 --- a/spring-jcl/src/main/java/org/apache/commons/logging/LogFactoryService.java +++ b/spring-jcl/src/main/java/org/apache/commons/logging/LogFactoryService.java @@ -80,4 +80,8 @@ public class LogFactoryService extends LogFactory { return this.attributes.keySet().toArray(new String[0]); } + @Override + public void release() { + } + } From 78fce80c430c6f1e75b060c192ea4986729e68f1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Sep 2023 17:36:32 +0200 Subject: [PATCH 3/4] AnnotationUtils.clearCache() includes all annotation caches Closes gh-31170 --- .../context/support/AbstractApplicationContext.java | 3 +++ .../springframework/core/annotation/AnnotationUtils.java | 3 +++ .../springframework/core/annotation/AttributeMethods.java | 6 ++---- .../org/springframework/core/annotation/OrderUtils.java | 4 ++-- .../core/annotation/RepeatableContainers.java | 4 ++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index 62eafb34da7..4ef8c78b27b 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -1081,6 +1081,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader // Let subclasses do some final clean-up if they wish... onClose(); + // Reset common introspection caches to avoid class reference leaks. + resetCommonCaches(); + // Reset local application listeners to pre-refresh state. if (this.earlyApplicationListeners != null) { this.applicationListeners.clear(); diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 1818174ef98..47baaae305e 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1326,6 +1326,9 @@ public abstract class AnnotationUtils { public static void clearCache() { AnnotationTypeMappings.clearCache(); AnnotationsScanner.clearCache(); + AttributeMethods.cache.clear(); + RepeatableContainers.cache.clear(); + OrderUtils.orderCache.clear(); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java b/spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java index dc122981aa0..a828ebe44b5 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,9 +39,7 @@ final class AttributeMethods { static final AttributeMethods NONE = new AttributeMethods(null, new Method[0]); - - private static final Map, AttributeMethods> cache = - new ConcurrentReferenceHashMap<>(); + static final Map, AttributeMethods> cache = new ConcurrentReferenceHashMap<>(); private static final Comparator methodComparator = (m1, m2) -> { if (m1 != null && m2 != null) { diff --git a/spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java index bc38dcec195..19fbc577b88 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ public abstract class OrderUtils { private static final String JAVAX_PRIORITY_ANNOTATION = "jakarta.annotation.Priority"; /** Cache for @Order value (or NOT_ANNOTATED marker) per Class. */ - private static final Map orderCache = new ConcurrentReferenceHashMap<>(64); + static final Map orderCache = new ConcurrentReferenceHashMap<>(64); /** diff --git a/spring-core/src/main/java/org/springframework/core/annotation/RepeatableContainers.java b/spring-core/src/main/java/org/springframework/core/annotation/RepeatableContainers.java index 1b0293f05df..adc33e3aa2e 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/RepeatableContainers.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/RepeatableContainers.java @@ -43,6 +43,8 @@ import org.springframework.util.ObjectUtils; */ public abstract class RepeatableContainers { + static final Map, Object> cache = new ConcurrentReferenceHashMap<>(); + @Nullable private final RepeatableContainers parent; @@ -141,8 +143,6 @@ public abstract class RepeatableContainers { */ private static class StandardRepeatableContainers extends RepeatableContainers { - private static final Map, Object> cache = new ConcurrentReferenceHashMap<>(); - private static final Object NONE = new Object(); private static final StandardRepeatableContainers INSTANCE = new StandardRepeatableContainers(); From db7654225eb23c5f76469484b32d8b7d295b04e2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Sep 2023 17:36:57 +0200 Subject: [PATCH 4/4] Polishing --- .../jdbc/core/JdbcTemplateQueryTests.java | 4 +- .../jdbc/core/JdbcTemplateTests.java | 62 +++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java index 435fe9e2d58..2bf1b7f6207 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java @@ -50,10 +50,10 @@ import static org.mockito.Mockito.verify; */ public class JdbcTemplateQueryTests { - private Connection connection = mock(); - private DataSource dataSource = mock(); + private Connection connection = mock(); + private Statement statement = mock(); private PreparedStatement preparedStatement = mock(); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java index 00eb07f0c3f..d952d473caa 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java @@ -75,18 +75,18 @@ import static org.mockito.Mockito.verify; */ public class JdbcTemplateTests { - private Connection connection = mock(); - private DataSource dataSource = mock(); + private Connection connection = mock(); + private Statement statement = mock(); private PreparedStatement preparedStatement = mock(); - private ResultSet resultSet = mock(); - private CallableStatement callableStatement = mock(); + private ResultSet resultSet = mock(); + private JdbcTemplate template = new JdbcTemplate(this.dataSource); @@ -136,9 +136,9 @@ public class JdbcTemplateTests { given(this.preparedStatement.executeUpdate()).willThrow(sqlException); Dispatcher d = new Dispatcher(idParam, sql); - assertThatExceptionOfType(UncategorizedSQLException.class).isThrownBy(() -> - this.template.update(d)) - .withCause(sqlException); + assertThatExceptionOfType(UncategorizedSQLException.class) + .isThrownBy(() -> this.template.update(d)) + .withCause(sqlException); verify(this.preparedStatement).setInt(1, idParam); verify(this.preparedStatement).close(); verify(this.connection, atLeastOnce()).close(); @@ -371,9 +371,9 @@ public class JdbcTemplateTests { given(this.statement.executeUpdate(sql)).willThrow(sqlException); given(this.connection.createStatement()).willReturn(this.statement); - assertThatExceptionOfType(DataAccessException.class).isThrownBy(() -> - this.template.update(sql)) - .withCause(sqlException); + assertThatExceptionOfType(DataAccessException.class) + .isThrownBy(() -> this.template.update(sql)) + .withCause(sqlException); verify(this.statement).close(); verify(this.connection, atLeastOnce()).close(); } @@ -672,9 +672,9 @@ public class JdbcTemplateTests { }; try { - assertThatExceptionOfType(DataAccessException.class).isThrownBy(() -> - this.template.batchUpdate(sql, setter)) - .withCause(sqlException); + assertThatExceptionOfType(DataAccessException.class) + .isThrownBy(() -> this.template.batchUpdate(sql, setter)) + .withCause(sqlException); } finally { verify(this.preparedStatement, times(2)).addBatch(); @@ -776,9 +776,9 @@ public class JdbcTemplateTests { JdbcTemplate template = new JdbcTemplate(this.dataSource, false); RowCountCallbackHandler rcch = new RowCountCallbackHandler(); - assertThatExceptionOfType(CannotGetJdbcConnectionException.class).isThrownBy(() -> - template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch)) - .withCause(sqlException); + assertThatExceptionOfType(CannotGetJdbcConnectionException.class) + .isThrownBy(() -> template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch)) + .withCause(sqlException); } @Test @@ -790,9 +790,9 @@ public class JdbcTemplateTests { this.template.afterPropertiesSet(); RowCountCallbackHandler rcch = new RowCountCallbackHandler(); - assertThatExceptionOfType(CannotGetJdbcConnectionException.class).isThrownBy(() -> - this.template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch)) - .withCause(sqlException); + assertThatExceptionOfType(CannotGetJdbcConnectionException.class) + .isThrownBy(() -> this.template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch)) + .withCause(sqlException); } @Test @@ -830,9 +830,9 @@ public class JdbcTemplateTests { this.template.afterPropertiesSet(); } RowCountCallbackHandler rcch = new RowCountCallbackHandler(); - assertThatExceptionOfType(CannotGetJdbcConnectionException.class).isThrownBy(() -> - this.template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch)) - .withCause(sqlException); + assertThatExceptionOfType(CannotGetJdbcConnectionException.class) + .isThrownBy(() -> this.template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch)) + .withCause(sqlException); } @Test @@ -859,9 +859,9 @@ public class JdbcTemplateTests { given(this.preparedStatement.executeUpdate()).willThrow(sqlException); PreparedStatementSetter pss = ps -> ps.setString(1, name); - assertThatExceptionOfType(DataAccessException.class).isThrownBy(() -> - new JdbcTemplate(this.dataSource).update(sql, pss)) - .withCause(sqlException); + assertThatExceptionOfType(DataAccessException.class) + .isThrownBy(() -> new JdbcTemplate(this.dataSource).update(sql, pss)) + .withCause(sqlException); verify(this.preparedStatement).setString(1, name); verify(this.preparedStatement).close(); verify(this.connection, atLeastOnce()).close(); @@ -897,9 +897,9 @@ public class JdbcTemplateTests { t.setIgnoreWarnings(false); ResultSetExtractor extractor = rs -> rs.getByte(1); - assertThatExceptionOfType(SQLWarningException.class).isThrownBy(() -> - t.query(sql, extractor)) - .withCause(warnings); + assertThatExceptionOfType(SQLWarningException.class) + .isThrownBy(() -> t.query(sql, extractor)) + .withCause(warnings); verify(this.resultSet).close(); verify(this.preparedStatement).close(); verify(this.connection).close(); @@ -940,7 +940,7 @@ public class JdbcTemplateTests { this.template.query(sql, (RowCallbackHandler) rs -> { throw sqlException; })) - .withCause(sqlException); + .withCause(sqlException); verify(this.resultSet).close(); verify(this.preparedStatement).close(); verify(this.connection, atLeastOnce()).close(); @@ -960,7 +960,7 @@ public class JdbcTemplateTests { this.template.query(sql, (RowCallbackHandler) rs -> { throw sqlException; })) - .withCause(sqlException); + .withCause(sqlException); verify(this.resultSet).close(); verify(this.preparedStatement).close(); verify(this.connection).close(); @@ -990,7 +990,7 @@ public class JdbcTemplateTests { template.query(sql, (RowCallbackHandler) rs -> { throw sqlException; })) - .withCause(sqlException); + .withCause(sqlException); verify(this.resultSet).close(); verify(this.preparedStatement).close(); verify(this.connection).close();