From 295f7093b1fa3762003309d9e36229d8316185ef Mon Sep 17 00:00:00 2001 From: Chengang Guan Date: Wed, 9 Sep 2026 15:54:26 +0800 Subject: [PATCH 1/4] Lazy handling of early setClientInfo/setNetworkTimeout calls in LazyConnectionDataSourceProxy Extend LazyConnectionInvocationHandler to cache early calls to: - setClientInfo(String, String) - setNetworkTimeout(Executor, int) These methods now defer physical connection acquisition until Statement creation, consistent with existing lazy behavior for autoCommit, readOnly, transactionIsolation, catalog, and schema. Accept and lazily cache calls to setNetworkTimeout even when the provided Executor is null. Since some JDBC driver implementations completely ignore the Executor parameter (or fall back to a default executor), we cannot meaningfully validate or handle a null Executor before the physical connection is obtained. getClientInfo() and getClientInfo(String) remains non-lazy (triggers immediate connection fetch)because it is a read operation whose value cannot be reliably cached due to driver defaults, pooled connection remnants, or external session modifications. setClientInfo(Properties) remains non-lazy. The reason is that JDBC driver implementations are inconsistent. Some treat it as overwrite, others as append/merge. To guarantee behavior identical to non-lazy execution across all driver, we choose not to cache or replay it, avoiding any risk of semantic mismatch. Closes gh-37258 Signed-off-by: Chengang Guan --- .../LazyConnectionDataSourceProxy.java | 35 ++ .../LazyConnectionDataSourceProxyTests.java | 469 ++++++++++++++++++ 2 files changed, 504 insertions(+) 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 8a9241c10e12..8b8afa87a131 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 @@ -22,7 +22,9 @@ import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.concurrent.Executor; import javax.sql.DataSource; @@ -86,6 +88,7 @@ * * @author Juergen Hoeller * @author Sam Brannen + * @author Chengang Guan * @since 1.1.4 * @see DataSourceTransactionManager * @see #setTargetDataSource @@ -311,6 +314,12 @@ private class LazyConnectionInvocationHandler implements InvocationHandler { private @Nullable Boolean autoCommit; + private @Nullable Executor networkTimeoutExecutor; + + private @Nullable Integer networkTimeout; + + private @Nullable Map clientInfo; + private boolean closed = false; private @Nullable Connection target; @@ -434,6 +443,24 @@ public LazyConnectionInvocationHandler(String username, String password) { // Ignore: no warnings to expose yet. return null; } + case "setNetworkTimeout" -> { + this.networkTimeoutExecutor = (Executor) args[0]; + this.networkTimeout = (Integer) args[1]; + return null; + } + case "getNetworkTimeout" -> { + return this.networkTimeout == null ? 0 : networkTimeout; + } + case "setClientInfo" -> { + if (args.length == 2) { + if (this.clientInfo == null) { + this.clientInfo = new LinkedHashMap<>(); + } + this.clientInfo.put((String) args[0], (String) args[1]); + return null; + } + // setClientInfo(Properties) will fall-through + } case "close" -> { // Ignore: no target connection yet. this.closed = true; @@ -530,6 +557,14 @@ private Connection getTargetConnection(Method operation) throws Throwable { if (this.autoCommit != null && this.autoCommit != defaultAutoCommit()) { target.setAutoCommit(this.autoCommit); } + if (this.networkTimeout != null) { + target.setNetworkTimeout(this.networkTimeoutExecutor, networkTimeout); + } + if (this.clientInfo != null) { + for (Map.Entry entry: clientInfo.entrySet()) { + target.setClientInfo(entry.getKey(), entry.getValue()); + } + } } catch (Throwable settingsEx) { logger.debug("Failed to apply transaction settings to JDBC Connection", settingsEx); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java index 943a8075aece..d15fb8b2e895 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java @@ -17,16 +17,36 @@ package org.springframework.jdbc.datasource; import java.lang.reflect.Field; +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; import java.util.Arrays; import java.util.HashSet; +import java.util.Map; +import java.util.Properties; import java.util.Set; +import java.util.concurrent.Executor; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.springframework.util.ReflectionUtils; +import javax.sql.DataSource; + import static java.sql.Connection.TRANSACTION_NONE; import static java.sql.Connection.TRANSACTION_READ_COMMITTED; import static java.sql.Connection.TRANSACTION_READ_UNCOMMITTED; @@ -34,11 +54,14 @@ import static java.sql.Connection.TRANSACTION_SERIALIZABLE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Tests for {@link LazyConnectionDataSourceProxy}. * * @author Sam Brannen + * @author Chengang Guan * @since 6.1 */ class LazyConnectionDataSourceProxyTests { @@ -94,6 +117,136 @@ void setDefaultTransactionIsolation() { assertThat(proxy.defaultTransactionIsolation()).isEqualTo(TRANSACTION_SERIALIZABLE); } + @Test + void lazyHandingCatalog() throws SQLException { + DataSource mockDataSource = mock(); + Connection physicalConnection = new MockConnection(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection); + proxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection = proxy.getConnection(); + lazyConnection.setCatalog("catalogName"); + assertThat(lazyConnection.getCatalog()).isEqualTo("catalogName"); + assertThat(physicalConnection.getCatalog()).isNull(); + establishPhysicalConnection(lazyConnection); + assertThat(lazyConnection.getCatalog()).isEqualTo("catalogName"); + assertThat(physicalConnection.getCatalog()).isEqualTo("catalogName"); + } + + @Test + void lazyHandingSchema() throws SQLException { + DataSource mockDataSource = mock(); + Connection physicalConnection = new MockConnection(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection); + proxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection = proxy.getConnection(); + lazyConnection.setSchema("schemaName"); + assertThat(lazyConnection.getSchema()).isEqualTo("schemaName"); + assertThat(physicalConnection.getSchema()).isNull(); + establishPhysicalConnection(lazyConnection); + assertThat(lazyConnection.getSchema()).isEqualTo("schemaName"); + assertThat(physicalConnection.getSchema()).isEqualTo("schemaName"); + } + + @Test + void lazyHandingHoldability() throws SQLException { + DataSource mockDataSource = mock(); + Connection physicalConnection = new MockConnection(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection); + proxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection = proxy.getConnection(); + lazyConnection.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertThat(lazyConnection.getHoldability()).isEqualTo(ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertThat(physicalConnection.getHoldability()).isEqualTo(0); + establishPhysicalConnection(lazyConnection); + assertThat(lazyConnection.getHoldability()).isEqualTo(ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertThat(physicalConnection.getHoldability()).isEqualTo(ResultSet.CLOSE_CURSORS_AT_COMMIT); + } + + @Test + void lazyHandingTransactionIsolation() throws SQLException { + DataSource mockDataSource = mock(); + Connection physicalConnection = new MockConnection(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection); + proxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection = proxy.getConnection(); + lazyConnection.setTransactionIsolation(TRANSACTION_SERIALIZABLE); + assertThat(lazyConnection.getTransactionIsolation()).isEqualTo(TRANSACTION_SERIALIZABLE); + assertThat(physicalConnection.getTransactionIsolation()).isEqualTo(0); + establishPhysicalConnection(lazyConnection); + assertThat(lazyConnection.getTransactionIsolation()).isEqualTo(TRANSACTION_SERIALIZABLE); + assertThat(physicalConnection.getTransactionIsolation()).isEqualTo(TRANSACTION_SERIALIZABLE); + } + + @Test + void lazyHandingAutoCommit() throws SQLException { + DataSource mockDataSource = mock(); + Connection physicalConnection = new MockConnection(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection); + proxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection = proxy.getConnection(); + lazyConnection.setAutoCommit(true); + assertThat(lazyConnection.getAutoCommit()).isTrue(); + assertThat(physicalConnection.getAutoCommit()).isFalse(); + establishPhysicalConnection(lazyConnection); + assertThat(lazyConnection.getAutoCommit()).isTrue(); + assertThat(physicalConnection.getAutoCommit()).isTrue(); + } + + @Test + void lazyHandingNetworkTimeoutExecutor() throws SQLException { + DataSource mockDataSource = mock(); + Connection physicalConnection = new MockConnection(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection); + proxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection = proxy.getConnection(); + lazyConnection.setNetworkTimeout(command -> {}, 1000); + assertThat(lazyConnection.getNetworkTimeout()).isEqualTo(1000); + assertThat(physicalConnection.getNetworkTimeout()).isEqualTo(0); + establishPhysicalConnection(lazyConnection); + assertThat(lazyConnection.getNetworkTimeout()).isEqualTo(1000); + assertThat(physicalConnection.getNetworkTimeout()).isEqualTo(1000); + } + + @Test + void lazyHandingClientInfoForKV() throws SQLException { + DataSource mockDataSource = mock(); + Connection physicalConnection = new MockConnection(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection); + proxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection = proxy.getConnection(); + lazyConnection.setClientInfo("k1", "v1"); + lazyConnection.setClientInfo("k2", "v2"); + assertThat(physicalConnection.getClientInfo("k1")).isNull(); + assertThat(physicalConnection.getClientInfo("k2")).isNull(); + assertThat(lazyConnection.getClientInfo("k1")).isEqualTo("v1"); // establishPhysicalConnection + assertThat(lazyConnection.getClientInfo("k2")).isEqualTo("v2"); + } + + @Test + void notLazyHandingClientInfoForProperties() throws SQLException { + DataSource mockDataSource = mock(); + Connection physicalConnection = new MockConnection(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection); + proxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection = proxy.getConnection(); + Properties properties = new Properties(); + properties.setProperty("k1", "v1"); + properties.setProperty("k2", "v2"); + lazyConnection.setClientInfo(properties); // establishPhysicalConnection + assertThat(physicalConnection.getClientInfo("k1")).isEqualTo("v1"); + assertThat(physicalConnection.getClientInfo("k2")).isEqualTo("v2"); + assertThat(lazyConnection.getClientInfo("k1")).isEqualTo("v1"); + assertThat(lazyConnection.getClientInfo("k2")).isEqualTo("v2"); + } + private static Stream streamIsolationConstants() { return Arrays.stream(Connection.class.getFields()) @@ -102,4 +255,320 @@ private static Stream streamIsolationConstants() { .filter(name -> name.startsWith("TRANSACTION_")); } + private static void establishPhysicalConnection(Connection lazyConnection) throws SQLException { + lazyConnection.prepareStatement("SELECT 1"); + } + + + /** + * A rudimentary physical connection implementation for testing lazy-loading behavior. + */ + static class MockConnection implements Connection { + + private String username; + + private String password; + + private String catalog; + + private String schema; + + private int holdability; + + private int transactionIsolation; + + private boolean autoCommit; + + private int networkTimeout; + + private Properties clientInfo; + + private boolean closed = false; + + public MockConnection() { + } + + public MockConnection(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public Statement createStatement() throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + return null; + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + return null; + } + + @Override + public String nativeSQL(String sql) throws SQLException { + return ""; + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + this.autoCommit = autoCommit; + } + + @Override + public boolean getAutoCommit() throws SQLException { + return this.autoCommit; + } + + @Override + public void commit() throws SQLException { + } + + @Override + public void rollback() throws SQLException { + } + + @Override + public void close() throws SQLException { + this.closed = true; + } + + @Override + public boolean isClosed() throws SQLException { + return this.closed; + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return null; + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + } + + @Override + public boolean isReadOnly() throws SQLException { + return false; + } + + @Override + public void setCatalog(String catalog) throws SQLException { + this.catalog = catalog; + } + + @Override + public String getCatalog() throws SQLException { + return this.catalog; + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + this.transactionIsolation = level; + } + + @Override + public int getTransactionIsolation() throws SQLException { + return this.transactionIsolation; + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return null; + } + + @Override + public void clearWarnings() throws SQLException { + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return null; + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return null; + } + + @Override + public Map> getTypeMap() throws SQLException { + return Map.of(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + } + + @Override + public void setHoldability(int holdability) throws SQLException { + this.holdability = holdability; + } + + @Override + public int getHoldability() throws SQLException { + return this.holdability; + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return null; + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return null; + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return null; + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return null; + } + + @Override + public Clob createClob() throws SQLException { + return null; + } + + @Override + public Blob createBlob() throws SQLException { + return null; + } + + @Override + public NClob createNClob() throws SQLException { + return null; + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return null; + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return false; + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + if (this.clientInfo == null) { + this.clientInfo = new Properties(); + } + this.clientInfo.put(name, value); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + if (properties != null) { + this.clientInfo = properties; + } + } + + @Override + public String getClientInfo(String name) throws SQLException { + if (this.clientInfo != null) { + return this.clientInfo.getProperty(name); + } + return null; + } + + @Override + public Properties getClientInfo() throws SQLException { + if (this.clientInfo == null) { + this.clientInfo = new Properties(); + } + return this.clientInfo; + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return null; + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return null; + } + + @Override + public void setSchema(String schema) throws SQLException { + this.schema = schema; + } + + @Override + public String getSchema() throws SQLException { + return this.schema; + } + + @Override + public void abort(Executor executor) throws SQLException { + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + if (executor == null) { + throw new SQLClientInfoException(); + } + this.networkTimeout = milliseconds; + } + + @Override + public int getNetworkTimeout() throws SQLException { + return this.networkTimeout; + } + + @Override + public T unwrap(Class iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } + + } + } From 6699522ac212bb5edc1e10d7c65b74765c76f60a Mon Sep 17 00:00:00 2001 From: Chengang Guan Date: Wed, 9 Sep 2026 16:53:30 +0800 Subject: [PATCH 2/4] Add the omitted this Signed-off-by: Chengang Guan --- .../jdbc/datasource/LazyConnectionDataSourceProxy.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 8b8afa87a131..83fc692e324f 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 @@ -449,7 +449,7 @@ public LazyConnectionInvocationHandler(String username, String password) { return null; } case "getNetworkTimeout" -> { - return this.networkTimeout == null ? 0 : networkTimeout; + return this.networkTimeout == null ? 0 : this.networkTimeout; } case "setClientInfo" -> { if (args.length == 2) { @@ -558,10 +558,10 @@ private Connection getTargetConnection(Method operation) throws Throwable { target.setAutoCommit(this.autoCommit); } if (this.networkTimeout != null) { - target.setNetworkTimeout(this.networkTimeoutExecutor, networkTimeout); + target.setNetworkTimeout(this.networkTimeoutExecutor, this.networkTimeout); } if (this.clientInfo != null) { - for (Map.Entry entry: clientInfo.entrySet()) { + for (Map.Entry entry: this.clientInfo.entrySet()) { target.setClientInfo(entry.getKey(), entry.getValue()); } } From 92872f1743c4c5f31f90d1a9039178d21a98c291 Mon Sep 17 00:00:00 2001 From: Chengang Guan Date: Thu, 10 Sep 2026 14:51:00 +0800 Subject: [PATCH 3/4] Use Mockito instead of custom MockConnection Signed-off-by: Chengang Guan --- .../LazyConnectionDataSourceProxy.java | 42 +- .../LazyConnectionDataSourceProxyTests.java | 530 ++++-------------- 2 files changed, 155 insertions(+), 417 deletions(-) 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 83fc692e324f..fdd2bea0fb06 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 @@ -37,12 +37,36 @@ /** * Proxy for a target DataSource, fetching actual JDBC Connections lazily, * i.e. not until first creation of a Statement. Connection initialization - * properties like auto-commit mode, transaction isolation and read-only mode - * will be kept and applied to the actual JDBC Connection as soon as an actual - * Connection is fetched (if ever). Consequently, commit and rollback calls will - * be ignored if no Statements have been created. As of 6.1.2, there is also - * special support for a {@link #setReadOnlyDataSource read-only DataSource} to use - * during a read-only transaction, in addition to the regular target DataSource. + * properties like auto-commit mode, transaction isolation, read-only mode, + * catalog, schema, holdability, client info and network timeout will be kept + * and applied to the actual JDBC Connection as soon as an actual Connection + * is fetched (if ever). Consequently, commit and rollback calls will be ignored + * if no Statements have been created. + * + *

Once a properties has been set, the corresponding getter method returns the + * set value until the actual Connection is fetched. If the property has not been + * set, invoking the getter triggers a fetch of the actual connection in order to + * obtain the default value. + * + *

Although client info is listed among the deferred properties above, + * the following methods are exceptions to the lazy acquisition behavior and + * force immediate acquisition of the underlying Connection. + * The {@link java.sql.Connection#getClientInfo()} and + * {@link java.sql.Connection#getClientInfo(java.lang.String)} + * methods are read operations whose values cannot be reliably cached due to + * driver defaults, remnants from pooled connections, or external session + * modifications. + * + *

The{@link java.sql.Connection#setClientInfo(java.util.Properties)} + * method also forces immediate acquisition. JDBC driver implementations are + * inconsistent: some treat it as an overwrite, while others treat it as an + * append/merge. To guarantee behavior identical to that of a non-lazy DataSource + * across all drivers, the proxy does not cache or replay it, thereby avoiding any + * risk of semantic mismatch. + * + *

As of 6.1.2, there is also special support for a + * {@link #setReadOnlyDataSource read-only DataSource} to use during a + * read-only transaction, in addition to the regular target DataSource. * *

This DataSource proxy allows to avoid fetching JDBC Connections from * a pool unless actually necessary. JDBC transaction control can happen @@ -449,7 +473,10 @@ public LazyConnectionInvocationHandler(String username, String password) { return null; } case "getNetworkTimeout" -> { - return this.networkTimeout == null ? 0 : this.networkTimeout; + if (this.networkTimeout != null) { + return this.networkTimeout; + } + // Else fetch actual Connection and check there. } case "setClientInfo" -> { if (args.length == 2) { @@ -459,6 +486,7 @@ public LazyConnectionInvocationHandler(String username, String password) { this.clientInfo.put((String) args[0], (String) args[1]); return null; } + // Else fetch actual Connection and check there. // setClientInfo(Properties) will fall-through } case "close" -> { diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java index d15fb8b2e895..8dd34f5027ca 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java @@ -17,44 +17,37 @@ package org.springframework.jdbc.datasource; import java.lang.reflect.Field; -import java.sql.Array; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.NClob; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLClientInfoException; import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Savepoint; -import java.sql.Statement; -import java.sql.Struct; import java.util.Arrays; import java.util.HashSet; -import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.Executor; import java.util.stream.Stream; +import javax.sql.DataSource; + +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.util.ReflectionUtils; -import javax.sql.DataSource; - import static java.sql.Connection.TRANSACTION_NONE; import static java.sql.Connection.TRANSACTION_READ_COMMITTED; import static java.sql.Connection.TRANSACTION_READ_UNCOMMITTED; import static java.sql.Connection.TRANSACTION_REPEATABLE_READ; import static java.sql.Connection.TRANSACTION_SERIALIZABLE; +import static java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** @@ -68,6 +61,14 @@ class LazyConnectionDataSourceProxyTests { private final LazyConnectionDataSourceProxy proxy = new LazyConnectionDataSourceProxy(); + private final LazyConnectionDataSourceProxy lazyProxy = new LazyConnectionDataSourceProxy(); + + + @BeforeEach + void setup() { + lazyProxy.setDefaultAutoCommit(false); + lazyProxy.setDefaultTransactionIsolation(TRANSACTION_READ_UNCOMMITTED); + } @Test void setDefaultTransactionIsolationNameToUnsupportedValues() { @@ -118,133 +119,154 @@ void setDefaultTransactionIsolation() { } @Test - void lazyHandingCatalog() throws SQLException { + void lazyHandlingCatalog() throws SQLException { DataSource mockDataSource = mock(); - Connection physicalConnection = new MockConnection(); - when(mockDataSource.getConnection()).thenReturn(physicalConnection); - proxy.setTargetDataSource(mockDataSource); - - Connection lazyConnection = proxy.getConnection(); - lazyConnection.setCatalog("catalogName"); - assertThat(lazyConnection.getCatalog()).isEqualTo("catalogName"); - assertThat(physicalConnection.getCatalog()).isNull(); - establishPhysicalConnection(lazyConnection); - assertThat(lazyConnection.getCatalog()).isEqualTo("catalogName"); - assertThat(physicalConnection.getCatalog()).isEqualTo("catalogName"); + Connection physicalConnection1 = mock(); + Connection physicalConnection2 = mock(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection1).thenReturn(physicalConnection2); + lazyProxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection1 = lazyProxy.getConnection(); + lazyConnection1.setCatalog("catalogName"); + assertThat(lazyConnection1.getCatalog()).isEqualTo("catalogName"); + verify(physicalConnection1,never()).setCatalog("catalogName"); + verify(physicalConnection1,never()).getCatalog(); + establishPhysicalConnection(lazyConnection1); + verify(physicalConnection1).setCatalog("catalogName"); + + Connection lazyConnection2 = lazyProxy.getConnection(); + lazyConnection2.getCatalog(); // establish physical connection immediately + verify(physicalConnection2).getCatalog(); } @Test - void lazyHandingSchema() throws SQLException { + void lazyHandlingSchema() throws SQLException { DataSource mockDataSource = mock(); - Connection physicalConnection = new MockConnection(); - when(mockDataSource.getConnection()).thenReturn(physicalConnection); - proxy.setTargetDataSource(mockDataSource); - - Connection lazyConnection = proxy.getConnection(); - lazyConnection.setSchema("schemaName"); - assertThat(lazyConnection.getSchema()).isEqualTo("schemaName"); - assertThat(physicalConnection.getSchema()).isNull(); - establishPhysicalConnection(lazyConnection); - assertThat(lazyConnection.getSchema()).isEqualTo("schemaName"); - assertThat(physicalConnection.getSchema()).isEqualTo("schemaName"); + Connection physicalConnection1 = mock(); + Connection physicalConnection2 = mock(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection1).thenReturn(physicalConnection2); + lazyProxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection1 = lazyProxy.getConnection(); + lazyConnection1.setSchema("schemaName"); + assertThat(lazyConnection1.getSchema()).isEqualTo("schemaName"); + verify(physicalConnection1,never()).setSchema("schemaName"); + verify(physicalConnection1,never()).getSchema(); + establishPhysicalConnection(lazyConnection1); + verify(physicalConnection1).setSchema("schemaName"); + + Connection lazyConnection2 = lazyProxy.getConnection(); + lazyConnection2.getSchema(); // establish physical connection immediately + verify(physicalConnection2).getSchema(); } @Test - void lazyHandingHoldability() throws SQLException { + void lazyHandlingHoldability() throws SQLException { DataSource mockDataSource = mock(); - Connection physicalConnection = new MockConnection(); - when(mockDataSource.getConnection()).thenReturn(physicalConnection); - proxy.setTargetDataSource(mockDataSource); - - Connection lazyConnection = proxy.getConnection(); - lazyConnection.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); - assertThat(lazyConnection.getHoldability()).isEqualTo(ResultSet.CLOSE_CURSORS_AT_COMMIT); - assertThat(physicalConnection.getHoldability()).isEqualTo(0); - establishPhysicalConnection(lazyConnection); - assertThat(lazyConnection.getHoldability()).isEqualTo(ResultSet.CLOSE_CURSORS_AT_COMMIT); - assertThat(physicalConnection.getHoldability()).isEqualTo(ResultSet.CLOSE_CURSORS_AT_COMMIT); + Connection physicalConnection1 = mock(); + Connection physicalConnection2 = mock(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection1).thenReturn(physicalConnection2); + lazyProxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection1 = lazyProxy.getConnection(); + lazyConnection1.setHoldability(CLOSE_CURSORS_AT_COMMIT); + assertThat(lazyConnection1.getHoldability()).isEqualTo(CLOSE_CURSORS_AT_COMMIT); + verify(physicalConnection1,never()).setHoldability(CLOSE_CURSORS_AT_COMMIT); + verify(physicalConnection1,never()).getHoldability(); + establishPhysicalConnection(lazyConnection1); + verify(physicalConnection1).setHoldability(CLOSE_CURSORS_AT_COMMIT); + + Connection lazyConnection2 = lazyProxy.getConnection(); + lazyConnection2.getHoldability(); // establish physical connection immediately + verify(physicalConnection2).getHoldability(); } @Test - void lazyHandingTransactionIsolation() throws SQLException { + void lazyHandlingTransactionIsolation() throws SQLException { DataSource mockDataSource = mock(); - Connection physicalConnection = new MockConnection(); + Connection physicalConnection = mock(); when(mockDataSource.getConnection()).thenReturn(physicalConnection); - proxy.setTargetDataSource(mockDataSource); + lazyProxy.setTargetDataSource(mockDataSource); - Connection lazyConnection = proxy.getConnection(); - lazyConnection.setTransactionIsolation(TRANSACTION_SERIALIZABLE); - assertThat(lazyConnection.getTransactionIsolation()).isEqualTo(TRANSACTION_SERIALIZABLE); - assertThat(physicalConnection.getTransactionIsolation()).isEqualTo(0); + Connection lazyConnection = lazyProxy.getConnection(); + lazyConnection.setTransactionIsolation(TRANSACTION_READ_COMMITTED); + assertThat(lazyConnection.getTransactionIsolation()).isEqualTo(TRANSACTION_READ_COMMITTED); + verify(physicalConnection,never()).setTransactionIsolation(TRANSACTION_READ_COMMITTED); + verify(physicalConnection,never()).getTransactionIsolation(); establishPhysicalConnection(lazyConnection); - assertThat(lazyConnection.getTransactionIsolation()).isEqualTo(TRANSACTION_SERIALIZABLE); - assertThat(physicalConnection.getTransactionIsolation()).isEqualTo(TRANSACTION_SERIALIZABLE); + verify(physicalConnection).setTransactionIsolation(TRANSACTION_READ_COMMITTED); } @Test - void lazyHandingAutoCommit() throws SQLException { + void lazyHandlingAutoCommit() throws SQLException { DataSource mockDataSource = mock(); - Connection physicalConnection = new MockConnection(); + Connection physicalConnection = mock(); when(mockDataSource.getConnection()).thenReturn(physicalConnection); - proxy.setTargetDataSource(mockDataSource); + lazyProxy.setTargetDataSource(mockDataSource); - Connection lazyConnection = proxy.getConnection(); + Connection lazyConnection = lazyProxy.getConnection(); lazyConnection.setAutoCommit(true); assertThat(lazyConnection.getAutoCommit()).isTrue(); - assertThat(physicalConnection.getAutoCommit()).isFalse(); + verify(physicalConnection,never()).setAutoCommit(true); + verify(physicalConnection,never()).getAutoCommit(); establishPhysicalConnection(lazyConnection); - assertThat(lazyConnection.getAutoCommit()).isTrue(); - assertThat(physicalConnection.getAutoCommit()).isTrue(); + verify(physicalConnection).setAutoCommit(true); } @Test - void lazyHandingNetworkTimeoutExecutor() throws SQLException { + void lazyHandlingNetworkTimeoutExecutor() throws SQLException { DataSource mockDataSource = mock(); - Connection physicalConnection = new MockConnection(); - when(mockDataSource.getConnection()).thenReturn(physicalConnection); - proxy.setTargetDataSource(mockDataSource); - - Connection lazyConnection = proxy.getConnection(); - lazyConnection.setNetworkTimeout(command -> {}, 1000); - assertThat(lazyConnection.getNetworkTimeout()).isEqualTo(1000); - assertThat(physicalConnection.getNetworkTimeout()).isEqualTo(0); - establishPhysicalConnection(lazyConnection); - assertThat(lazyConnection.getNetworkTimeout()).isEqualTo(1000); - assertThat(physicalConnection.getNetworkTimeout()).isEqualTo(1000); + Connection physicalConnection1 = mock(); + Connection physicalConnection2 = mock(); + Executor executor = mock(); + when(mockDataSource.getConnection()).thenReturn(physicalConnection1).thenReturn(physicalConnection2); + doThrow(SQLException.class).when(physicalConnection2).setNetworkTimeout(eq(null), anyInt()); + lazyProxy.setTargetDataSource(mockDataSource); + + Connection lazyConnection1 = lazyProxy.getConnection(); + lazyConnection1.setNetworkTimeout(executor, 1000); + assertThat(lazyConnection1.getNetworkTimeout()).isEqualTo(1000); + verify(physicalConnection1,never()).setNetworkTimeout(executor, 1000); + verify(physicalConnection1,never()).getNetworkTimeout(); + establishPhysicalConnection(lazyConnection1); + verify(physicalConnection1).setNetworkTimeout(executor, 1000); + + // null executor + Connection lazyConnection2 = lazyProxy.getConnection(); + lazyConnection2.setNetworkTimeout(null, 1000); + assertThatThrownBy(() -> establishPhysicalConnection(lazyConnection2)).isInstanceOf(SQLException.class); } @Test - void lazyHandingClientInfoForKV() throws SQLException { + void lazyHandlingClientInfoForKV() throws SQLException { DataSource mockDataSource = mock(); - Connection physicalConnection = new MockConnection(); + Connection physicalConnection = mock(); when(mockDataSource.getConnection()).thenReturn(physicalConnection); - proxy.setTargetDataSource(mockDataSource); + lazyProxy.setTargetDataSource(mockDataSource); - Connection lazyConnection = proxy.getConnection(); + Connection lazyConnection = lazyProxy.getConnection(); lazyConnection.setClientInfo("k1", "v1"); lazyConnection.setClientInfo("k2", "v2"); - assertThat(physicalConnection.getClientInfo("k1")).isNull(); - assertThat(physicalConnection.getClientInfo("k2")).isNull(); - assertThat(lazyConnection.getClientInfo("k1")).isEqualTo("v1"); // establishPhysicalConnection - assertThat(lazyConnection.getClientInfo("k2")).isEqualTo("v2"); + verify(physicalConnection, never()).setClientInfo("k1", "v1"); + verify(physicalConnection, never()).setClientInfo("k2", "v2"); + lazyConnection.getClientInfo("k1"); // establish physical connection immediately + verify(physicalConnection).setClientInfo("k1", "v1"); + verify(physicalConnection).getClientInfo("k1"); } @Test - void notLazyHandingClientInfoForProperties() throws SQLException { + void nonLazyHandlingClientInfoForProperties() throws SQLException { DataSource mockDataSource = mock(); - Connection physicalConnection = new MockConnection(); + Connection physicalConnection = mock(); when(mockDataSource.getConnection()).thenReturn(physicalConnection); - proxy.setTargetDataSource(mockDataSource); + lazyProxy.setTargetDataSource(mockDataSource); - Connection lazyConnection = proxy.getConnection(); + Connection lazyConnection = lazyProxy.getConnection(); Properties properties = new Properties(); properties.setProperty("k1", "v1"); properties.setProperty("k2", "v2"); - lazyConnection.setClientInfo(properties); // establishPhysicalConnection - assertThat(physicalConnection.getClientInfo("k1")).isEqualTo("v1"); - assertThat(physicalConnection.getClientInfo("k2")).isEqualTo("v2"); - assertThat(lazyConnection.getClientInfo("k1")).isEqualTo("v1"); - assertThat(lazyConnection.getClientInfo("k2")).isEqualTo("v2"); + lazyConnection.setClientInfo(properties); // establish physical connection immediately + verify(physicalConnection).setClientInfo(properties); } @@ -259,316 +281,4 @@ private static void establishPhysicalConnection(Connection lazyConnection) throw lazyConnection.prepareStatement("SELECT 1"); } - - /** - * A rudimentary physical connection implementation for testing lazy-loading behavior. - */ - static class MockConnection implements Connection { - - private String username; - - private String password; - - private String catalog; - - private String schema; - - private int holdability; - - private int transactionIsolation; - - private boolean autoCommit; - - private int networkTimeout; - - private Properties clientInfo; - - private boolean closed = false; - - public MockConnection() { - } - - public MockConnection(String username, String password) { - this.username = username; - this.password = password; - } - - @Override - public Statement createStatement() throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql) throws SQLException { - return null; - } - - @Override - public CallableStatement prepareCall(String sql) throws SQLException { - return null; - } - - @Override - public String nativeSQL(String sql) throws SQLException { - return ""; - } - - @Override - public void setAutoCommit(boolean autoCommit) throws SQLException { - this.autoCommit = autoCommit; - } - - @Override - public boolean getAutoCommit() throws SQLException { - return this.autoCommit; - } - - @Override - public void commit() throws SQLException { - } - - @Override - public void rollback() throws SQLException { - } - - @Override - public void close() throws SQLException { - this.closed = true; - } - - @Override - public boolean isClosed() throws SQLException { - return this.closed; - } - - @Override - public DatabaseMetaData getMetaData() throws SQLException { - return null; - } - - @Override - public void setReadOnly(boolean readOnly) throws SQLException { - } - - @Override - public boolean isReadOnly() throws SQLException { - return false; - } - - @Override - public void setCatalog(String catalog) throws SQLException { - this.catalog = catalog; - } - - @Override - public String getCatalog() throws SQLException { - return this.catalog; - } - - @Override - public void setTransactionIsolation(int level) throws SQLException { - this.transactionIsolation = level; - } - - @Override - public int getTransactionIsolation() throws SQLException { - return this.transactionIsolation; - } - - @Override - public SQLWarning getWarnings() throws SQLException { - return null; - } - - @Override - public void clearWarnings() throws SQLException { - } - - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return null; - } - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return null; - } - - @Override - public Map> getTypeMap() throws SQLException { - return Map.of(); - } - - @Override - public void setTypeMap(Map> map) throws SQLException { - } - - @Override - public void setHoldability(int holdability) throws SQLException { - this.holdability = holdability; - } - - @Override - public int getHoldability() throws SQLException { - return this.holdability; - } - - @Override - public Savepoint setSavepoint() throws SQLException { - return null; - } - - @Override - public Savepoint setSavepoint(String name) throws SQLException { - return null; - } - - @Override - public void rollback(Savepoint savepoint) throws SQLException { - } - - @Override - public void releaseSavepoint(Savepoint savepoint) throws SQLException { - } - - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return null; - } - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { - return null; - } - - @Override - public Clob createClob() throws SQLException { - return null; - } - - @Override - public Blob createBlob() throws SQLException { - return null; - } - - @Override - public NClob createNClob() throws SQLException { - return null; - } - - @Override - public SQLXML createSQLXML() throws SQLException { - return null; - } - - @Override - public boolean isValid(int timeout) throws SQLException { - return false; - } - - @Override - public void setClientInfo(String name, String value) throws SQLClientInfoException { - if (this.clientInfo == null) { - this.clientInfo = new Properties(); - } - this.clientInfo.put(name, value); - } - - @Override - public void setClientInfo(Properties properties) throws SQLClientInfoException { - if (properties != null) { - this.clientInfo = properties; - } - } - - @Override - public String getClientInfo(String name) throws SQLException { - if (this.clientInfo != null) { - return this.clientInfo.getProperty(name); - } - return null; - } - - @Override - public Properties getClientInfo() throws SQLException { - if (this.clientInfo == null) { - this.clientInfo = new Properties(); - } - return this.clientInfo; - } - - @Override - public Array createArrayOf(String typeName, Object[] elements) throws SQLException { - return null; - } - - @Override - public Struct createStruct(String typeName, Object[] attributes) throws SQLException { - return null; - } - - @Override - public void setSchema(String schema) throws SQLException { - this.schema = schema; - } - - @Override - public String getSchema() throws SQLException { - return this.schema; - } - - @Override - public void abort(Executor executor) throws SQLException { - } - - @Override - public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { - if (executor == null) { - throw new SQLClientInfoException(); - } - this.networkTimeout = milliseconds; - } - - @Override - public int getNetworkTimeout() throws SQLException { - return this.networkTimeout; - } - - @Override - public T unwrap(Class iface) throws SQLException { - return null; - } - - @Override - public boolean isWrapperFor(Class iface) throws SQLException { - return false; - } - - } - } From 563c8cae8f32499c61e3687b0bd0deb573efb835 Mon Sep 17 00:00:00 2001 From: Chengang Guan Date: Fri, 11 Sep 2026 08:06:27 +0800 Subject: [PATCH 4/4] Fix typo Signed-off-by: Chengang Guan --- .../LazyConnectionDataSourceProxy.java | 4 ++-- .../LazyConnectionDataSourceProxyTests.java | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) 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 fdd2bea0fb06..34c751d08c6a 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 @@ -43,7 +43,7 @@ * is fetched (if ever). Consequently, commit and rollback calls will be ignored * if no Statements have been created. * - *

Once a properties has been set, the corresponding getter method returns the + *

Once a property has been set, the corresponding getter method returns the * set value until the actual Connection is fetched. If the property has not been * set, invoking the getter triggers a fetch of the actual connection in order to * obtain the default value. @@ -57,7 +57,7 @@ * driver defaults, remnants from pooled connections, or external session * modifications. * - *

The{@link java.sql.Connection#setClientInfo(java.util.Properties)} + *

The {@link java.sql.Connection#setClientInfo(java.util.Properties)} * method also forces immediate acquisition. JDBC driver implementations are * inconsistent: some treat it as an overwrite, while others treat it as an * append/merge. To guarantee behavior identical to that of a non-lazy DataSource diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java index 8dd34f5027ca..47ac4d4bd561 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxyTests.java @@ -129,8 +129,8 @@ void lazyHandlingCatalog() throws SQLException { Connection lazyConnection1 = lazyProxy.getConnection(); lazyConnection1.setCatalog("catalogName"); assertThat(lazyConnection1.getCatalog()).isEqualTo("catalogName"); - verify(physicalConnection1,never()).setCatalog("catalogName"); - verify(physicalConnection1,never()).getCatalog(); + verify(physicalConnection1, never()).setCatalog("catalogName"); + verify(physicalConnection1, never()).getCatalog(); establishPhysicalConnection(lazyConnection1); verify(physicalConnection1).setCatalog("catalogName"); @@ -150,8 +150,8 @@ void lazyHandlingSchema() throws SQLException { Connection lazyConnection1 = lazyProxy.getConnection(); lazyConnection1.setSchema("schemaName"); assertThat(lazyConnection1.getSchema()).isEqualTo("schemaName"); - verify(physicalConnection1,never()).setSchema("schemaName"); - verify(physicalConnection1,never()).getSchema(); + verify(physicalConnection1, never()).setSchema("schemaName"); + verify(physicalConnection1, never()).getSchema(); establishPhysicalConnection(lazyConnection1); verify(physicalConnection1).setSchema("schemaName"); @@ -171,8 +171,8 @@ void lazyHandlingHoldability() throws SQLException { Connection lazyConnection1 = lazyProxy.getConnection(); lazyConnection1.setHoldability(CLOSE_CURSORS_AT_COMMIT); assertThat(lazyConnection1.getHoldability()).isEqualTo(CLOSE_CURSORS_AT_COMMIT); - verify(physicalConnection1,never()).setHoldability(CLOSE_CURSORS_AT_COMMIT); - verify(physicalConnection1,never()).getHoldability(); + verify(physicalConnection1, never()).setHoldability(CLOSE_CURSORS_AT_COMMIT); + verify(physicalConnection1, never()).getHoldability(); establishPhysicalConnection(lazyConnection1); verify(physicalConnection1).setHoldability(CLOSE_CURSORS_AT_COMMIT); @@ -191,8 +191,8 @@ void lazyHandlingTransactionIsolation() throws SQLException { Connection lazyConnection = lazyProxy.getConnection(); lazyConnection.setTransactionIsolation(TRANSACTION_READ_COMMITTED); assertThat(lazyConnection.getTransactionIsolation()).isEqualTo(TRANSACTION_READ_COMMITTED); - verify(physicalConnection,never()).setTransactionIsolation(TRANSACTION_READ_COMMITTED); - verify(physicalConnection,never()).getTransactionIsolation(); + verify(physicalConnection, never()).setTransactionIsolation(TRANSACTION_READ_COMMITTED); + verify(physicalConnection, never()).getTransactionIsolation(); establishPhysicalConnection(lazyConnection); verify(physicalConnection).setTransactionIsolation(TRANSACTION_READ_COMMITTED); } @@ -207,8 +207,8 @@ void lazyHandlingAutoCommit() throws SQLException { Connection lazyConnection = lazyProxy.getConnection(); lazyConnection.setAutoCommit(true); assertThat(lazyConnection.getAutoCommit()).isTrue(); - verify(physicalConnection,never()).setAutoCommit(true); - verify(physicalConnection,never()).getAutoCommit(); + verify(physicalConnection, never()).setAutoCommit(true); + verify(physicalConnection, never()).getAutoCommit(); establishPhysicalConnection(lazyConnection); verify(physicalConnection).setAutoCommit(true); } @@ -226,8 +226,8 @@ void lazyHandlingNetworkTimeoutExecutor() throws SQLException { Connection lazyConnection1 = lazyProxy.getConnection(); lazyConnection1.setNetworkTimeout(executor, 1000); assertThat(lazyConnection1.getNetworkTimeout()).isEqualTo(1000); - verify(physicalConnection1,never()).setNetworkTimeout(executor, 1000); - verify(physicalConnection1,never()).getNetworkTimeout(); + verify(physicalConnection1, never()).setNetworkTimeout(executor, 1000); + verify(physicalConnection1, never()).getNetworkTimeout(); establishPhysicalConnection(lazyConnection1); verify(physicalConnection1).setNetworkTimeout(executor, 1000);