From 6cea397bd8a58aaf5059c95d0067396483225e18 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 24 Mar 2026 23:40:43 +0100 Subject: [PATCH] Consistent handling of early setCatalog/setSchema/setHoldability calls Closes gh-36527 Closes gh-36528 --- .../LazyConnectionDataSourceProxy.java | 79 +++++++++++++------ .../DataSourceTransactionManagerTests.java | 23 +++++- 2 files changed, 78 insertions(+), 24 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 923cd817bae..8a9241c10e1 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 @@ -21,7 +21,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; @@ -300,13 +299,17 @@ public class LazyConnectionDataSourceProxy extends DelegatingDataSource { private @Nullable String password; - private @Nullable Boolean autoCommit; + private @Nullable String catalog; - private @Nullable Integer transactionIsolation; + private @Nullable String schema; + + private @Nullable Integer holdability; private boolean readOnly = false; - private int holdability = ResultSet.CLOSE_CURSORS_AT_COMMIT; + private @Nullable Integer transactionIsolation; + + private @Nullable Boolean autoCommit; private boolean closed = false; @@ -364,26 +367,34 @@ public class LazyConnectionDataSourceProxy extends DelegatingDataSource { case "toString" -> { return "Lazy Connection proxy for target DataSource [" + getTargetDataSource() + "]"; } - case "getAutoCommit" -> { - if (this.autoCommit != null) { - return this.autoCommit; + case "getCatalog" -> { + if (this.catalog != null) { + return this.catalog; } - // Else fetch actual Connection and check there, - // because we didn't have a default specified. + // Else fetch actual Connection and check there. } - case "setAutoCommit" -> { - this.autoCommit = (Boolean) args[0]; + case "setCatalog" -> { + this.catalog = (String) args[0]; return null; } - case "getTransactionIsolation" -> { - if (this.transactionIsolation != null) { - return this.transactionIsolation; + case "getSchema" -> { + if (this.schema != null) { + return this.schema; } - // Else fetch actual Connection and check there, - // because we didn't have a default specified. + // Else fetch actual Connection and check there. } - case "setTransactionIsolation" -> { - this.transactionIsolation = (Integer) args[0]; + case "setSchema" -> { + this.schema = (String) args[0]; + return null; + } + case "getHoldability" -> { + if (this.holdability != null) { + return this.holdability; + } + // Else fetch actual Connection and check there. + } + case "setHoldability" -> { + this.holdability = (Integer) args[0]; return null; } case "isReadOnly" -> { @@ -393,11 +404,26 @@ public class LazyConnectionDataSourceProxy extends DelegatingDataSource { this.readOnly = (Boolean) args[0]; return null; } - case "getHoldability" -> { - return this.holdability; + case "getTransactionIsolation" -> { + if (this.transactionIsolation != null) { + return this.transactionIsolation; + } + // Else fetch actual Connection and check there, + // because we didn't have a default specified. } - case "setHoldability" -> { - this.holdability = (Integer) args[0]; + case "setTransactionIsolation" -> { + this.transactionIsolation = (Integer) args[0]; + return null; + } + case "getAutoCommit" -> { + if (this.autoCommit != null) { + return this.autoCommit; + } + // Else fetch actual Connection and check there, + // because we didn't have a default specified. + } + case "setAutoCommit" -> { + this.autoCommit = (Boolean) args[0]; return null; } case "commit", "rollback" -> { @@ -485,6 +511,15 @@ public class LazyConnectionDataSourceProxy extends DelegatingDataSource { // Apply kept transaction settings, if any. try { + if (this.catalog != null) { + target.setCatalog(this.catalog); + } + if (this.schema != null) { + target.setSchema(this.schema); + } + if (this.holdability != null) { + target.setHoldability(this.holdability); + } if (this.readOnly && readOnlyDataSource == null) { DataSourceUtils.setReadOnlyIfPossible(target); } 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 d2689ca030f..c6a2f014569 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 @@ -19,6 +19,7 @@ package org.springframework.jdbc.datasource; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Savepoint; import java.sql.Statement; @@ -828,14 +829,32 @@ public class DataSourceTransactionManagerTests { dsProxy.setDefaultAutoCommit(true); dsProxy.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); dsProxy.afterPropertiesSet(); - tm = createTransactionManager(dsProxy); - try (Connection con = dsProxy.getConnection()) { + DelegatingDataSource dsAdapter = new DelegatingDataSource(dsProxy) { + @Override + public Connection getConnection() throws SQLException { + Connection con = super.getConnection(); + con.setCatalog("myCatalog"); + con.setSchema("mySchema"); + con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT); + return con; + } + }; + + tm = createTransactionManager(dsAdapter); + + try (Connection con = dsAdapter.getConnection()) { assertThat(con.isReadOnly()).isFalse(); + assertThat(con.getCatalog()).isEqualTo("myCatalog"); + assertThat(con.getSchema()).isEqualTo("mySchema"); + assertThat(con.getHoldability()).isEqualTo(ResultSet.HOLD_CURSORS_OVER_COMMIT); } assertTransactionReadOnly(TransactionDefinition.ISOLATION_SERIALIZABLE, true); InOrder ordered = inOrder(con); + ordered.verify(con).setCatalog("myCatalog"); + ordered.verify(con).setSchema("mySchema"); + ordered.verify(con).setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT); ordered.verify(con).setReadOnly(true); ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); ordered.verify(con).setAutoCommit(false);