Skip to content

Commit ab33000

Browse files
committed
Do not keep target connection after failed settings
Includes aligned setReadOnly exception suppression. Closes gh-35980
1 parent e2ab9cd commit ab33000

File tree

2 files changed

+63
-44
lines changed

2 files changed

+63
-44
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -200,24 +200,10 @@ private static Connection fetchConnection(DataSource dataSource) throws SQLExcep
200200
boolean debugEnabled = logger.isDebugEnabled();
201201
// Set read-only flag.
202202
if (setReadOnly) {
203-
try {
204-
if (debugEnabled) {
205-
logger.debug("Setting JDBC Connection [" + con + "] read-only");
206-
}
207-
con.setReadOnly(true);
208-
}
209-
catch (SQLException | RuntimeException ex) {
210-
Throwable exToCheck = ex;
211-
while (exToCheck != null) {
212-
if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
213-
// Assume it's a connection timeout that would otherwise get lost: for example, from JDBC 4.0
214-
throw ex;
215-
}
216-
exToCheck = exToCheck.getCause();
217-
}
218-
// "read-only not supported" SQLException -> ignore, it's just a hint anyway
219-
logger.debug("Could not set JDBC Connection read-only", ex);
203+
if (debugEnabled) {
204+
logger.debug("Setting JDBC Connection [" + con + "] read-only");
220205
}
206+
setReadOnlyIfPossible(con);
221207
}
222208

223209
// Apply specific isolation level, if any.
@@ -236,6 +222,31 @@ private static Connection fetchConnection(DataSource dataSource) throws SQLExcep
236222
return previousIsolationLevel;
237223
}
238224

225+
/**
226+
* Apply the read-only hint to the given Connection,
227+
* suppressing exceptions other than timeout-related ones.
228+
* @param con the Connection to prepare
229+
* @throws SQLException in case of a timeout exception
230+
* @since 6.2.15
231+
*/
232+
static void setReadOnlyIfPossible(Connection con) throws SQLException {
233+
try {
234+
con.setReadOnly(true);
235+
}
236+
catch (SQLException | RuntimeException ex) {
237+
Throwable exToCheck = ex;
238+
while (exToCheck != null) {
239+
if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
240+
// Assume it's a connection timeout that would otherwise get lost: for example, from JDBC 4.0
241+
throw ex;
242+
}
243+
exToCheck = exToCheck.getCause();
244+
}
245+
// "read-only not supported" SQLException -> ignore, it's just a hint anyway
246+
logger.debug("Could not set JDBC Connection read-only", ex);
247+
}
248+
}
249+
239250
/**
240251
* Reset the given Connection after a transaction,
241252
* regarding read-only flag and isolation level.

spring-jdbc/src/main/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxy.java

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -460,48 +460,56 @@ private boolean hasTargetConnection() {
460460
/**
461461
* Return the target Connection, fetching it and initializing it if necessary.
462462
*/
463-
private Connection getTargetConnection(Method operation) throws SQLException {
464-
if (this.target == null) {
465-
// No target Connection held -> fetch one.
463+
private Connection getTargetConnection(Method operation) throws Throwable {
464+
Connection target = this.target;
465+
if (target != null) {
466+
// Target Connection already held -> return it.
466467
if (logger.isTraceEnabled()) {
467-
logger.trace("Connecting to database for operation '" + operation.getName() + "'");
468+
logger.trace("Using existing database connection for operation '" + operation.getName() + "'");
468469
}
470+
return target;
471+
}
469472

470-
// Fetch physical Connection from DataSource.
471-
DataSource dataSource = getDataSourceToUse();
472-
this.target = (this.username != null ? dataSource.getConnection(this.username, this.password) :
473-
dataSource.getConnection());
474-
if (this.target == null) {
475-
throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource);
476-
}
473+
// No target Connection held -> fetch one.
474+
if (logger.isTraceEnabled()) {
475+
logger.trace("Connecting to database for operation '" + operation.getName() + "'");
476+
}
477+
478+
// Fetch physical Connection from DataSource.
479+
DataSource dataSource = getDataSourceToUse();
480+
target = (this.username != null ? dataSource.getConnection(this.username, this.password) :
481+
dataSource.getConnection());
482+
if (target == null) {
483+
throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource);
484+
}
477485

478-
// Apply kept transaction settings, if any.
486+
// Apply kept transaction settings, if any.
487+
try {
479488
if (this.readOnly && readOnlyDataSource == null) {
480-
try {
481-
this.target.setReadOnly(true);
482-
}
483-
catch (Exception ex) {
484-
// "read-only not supported" -> ignore, it's just a hint anyway
485-
logger.debug("Could not set JDBC Connection read-only", ex);
486-
}
489+
DataSourceUtils.setReadOnlyIfPossible(target);
487490
}
488491
if (this.transactionIsolation != null &&
489492
!this.transactionIsolation.equals(defaultTransactionIsolation())) {
490-
this.target.setTransactionIsolation(this.transactionIsolation);
493+
target.setTransactionIsolation(this.transactionIsolation);
491494
}
492495
if (this.autoCommit != null && this.autoCommit != defaultAutoCommit()) {
493-
this.target.setAutoCommit(this.autoCommit);
496+
target.setAutoCommit(this.autoCommit);
494497
}
495498
}
496-
497-
else {
498-
// Target Connection already held -> return it.
499-
if (logger.isTraceEnabled()) {
500-
logger.trace("Using existing database connection for operation '" + operation.getName() + "'");
499+
catch (Throwable settingsEx) {
500+
logger.debug("Failed to apply transaction settings to JDBC Connection", settingsEx);
501+
// Close Connection and do not set it as target.
502+
try {
503+
target.close();
504+
}
505+
catch (Throwable closeEx) {
506+
logger.debug("Could not close JDBC Connection after failed settings", closeEx);
501507
}
508+
throw settingsEx;
502509
}
503510

504-
return this.target;
511+
this.target = target;
512+
return target;
505513
}
506514

507515
private DataSource getDataSourceToUse() {

0 commit comments

Comments
 (0)