mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Do not keep target connection after failed settings
Includes aligned setReadOnly exception suppression.
Closes gh-35980
(cherry picked from commit ab33000750)
This commit is contained in:
+28
-17
@@ -202,24 +202,10 @@ public abstract class DataSourceUtils {
|
||||
boolean debugEnabled = logger.isDebugEnabled();
|
||||
// Set read-only flag.
|
||||
if (setReadOnly) {
|
||||
try {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Setting JDBC Connection [" + con + "] read-only");
|
||||
}
|
||||
con.setReadOnly(true);
|
||||
}
|
||||
catch (SQLException | RuntimeException ex) {
|
||||
Throwable exToCheck = ex;
|
||||
while (exToCheck != null) {
|
||||
if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
|
||||
// Assume it's a connection timeout that would otherwise get lost: for example, from JDBC 4.0
|
||||
throw ex;
|
||||
}
|
||||
exToCheck = exToCheck.getCause();
|
||||
}
|
||||
// "read-only not supported" SQLException -> ignore, it's just a hint anyway
|
||||
logger.debug("Could not set JDBC Connection read-only", ex);
|
||||
if (debugEnabled) {
|
||||
logger.debug("Setting JDBC Connection [" + con + "] read-only");
|
||||
}
|
||||
setReadOnlyIfPossible(con);
|
||||
}
|
||||
|
||||
// Apply specific isolation level, if any.
|
||||
@@ -238,6 +224,31 @@ public abstract class DataSourceUtils {
|
||||
return previousIsolationLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the read-only hint to the given Connection,
|
||||
* suppressing exceptions other than timeout-related ones.
|
||||
* @param con the Connection to prepare
|
||||
* @throws SQLException in case of a timeout exception
|
||||
* @since 6.2.15
|
||||
*/
|
||||
static void setReadOnlyIfPossible(Connection con) throws SQLException {
|
||||
try {
|
||||
con.setReadOnly(true);
|
||||
}
|
||||
catch (SQLException | RuntimeException ex) {
|
||||
Throwable exToCheck = ex;
|
||||
while (exToCheck != null) {
|
||||
if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
|
||||
// Assume it's a connection timeout that would otherwise get lost: for example, from JDBC 4.0
|
||||
throw ex;
|
||||
}
|
||||
exToCheck = exToCheck.getCause();
|
||||
}
|
||||
// "read-only not supported" SQLException -> ignore, it's just a hint anyway
|
||||
logger.debug("Could not set JDBC Connection read-only", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the given Connection after a transaction,
|
||||
* regarding read-only flag and isolation level.
|
||||
|
||||
+44
-36
@@ -471,48 +471,56 @@ public class LazyConnectionDataSourceProxy extends DelegatingDataSource {
|
||||
/**
|
||||
* Return the target Connection, fetching it and initializing it if necessary.
|
||||
*/
|
||||
private Connection getTargetConnection(Method operation) throws SQLException {
|
||||
if (this.target == null) {
|
||||
// No target Connection held -> fetch one.
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Connecting to database for operation '" + operation.getName() + "'");
|
||||
}
|
||||
|
||||
// Fetch physical Connection from DataSource.
|
||||
DataSource dataSource = getDataSourceToUse();
|
||||
this.target = (this.username != null ? dataSource.getConnection(this.username, this.password) :
|
||||
dataSource.getConnection());
|
||||
if (this.target == null) {
|
||||
throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource);
|
||||
}
|
||||
|
||||
// Apply kept transaction settings, if any.
|
||||
if (this.readOnly && readOnlyDataSource == null) {
|
||||
try {
|
||||
this.target.setReadOnly(true);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// "read-only not supported" -> ignore, it's just a hint anyway
|
||||
logger.debug("Could not set JDBC Connection read-only", ex);
|
||||
}
|
||||
}
|
||||
if (this.transactionIsolation != null &&
|
||||
!this.transactionIsolation.equals(defaultTransactionIsolation())) {
|
||||
this.target.setTransactionIsolation(this.transactionIsolation);
|
||||
}
|
||||
if (this.autoCommit != null && this.autoCommit != defaultAutoCommit()) {
|
||||
this.target.setAutoCommit(this.autoCommit);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
private Connection getTargetConnection(Method operation) throws Throwable {
|
||||
Connection target = this.target;
|
||||
if (target != null) {
|
||||
// Target Connection already held -> return it.
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Using existing database connection for operation '" + operation.getName() + "'");
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
return this.target;
|
||||
// No target Connection held -> fetch one.
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Connecting to database for operation '" + operation.getName() + "'");
|
||||
}
|
||||
|
||||
// Fetch physical Connection from DataSource.
|
||||
DataSource dataSource = getDataSourceToUse();
|
||||
target = (this.username != null ? dataSource.getConnection(this.username, this.password) :
|
||||
dataSource.getConnection());
|
||||
if (target == null) {
|
||||
throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource);
|
||||
}
|
||||
|
||||
// Apply kept transaction settings, if any.
|
||||
try {
|
||||
if (this.readOnly && readOnlyDataSource == null) {
|
||||
DataSourceUtils.setReadOnlyIfPossible(target);
|
||||
}
|
||||
if (this.transactionIsolation != null &&
|
||||
!this.transactionIsolation.equals(defaultTransactionIsolation())) {
|
||||
target.setTransactionIsolation(this.transactionIsolation);
|
||||
}
|
||||
if (this.autoCommit != null && this.autoCommit != defaultAutoCommit()) {
|
||||
target.setAutoCommit(this.autoCommit);
|
||||
}
|
||||
}
|
||||
catch (Throwable settingsEx) {
|
||||
logger.debug("Failed to apply transaction settings to JDBC Connection", settingsEx);
|
||||
// Close Connection and do not set it as target.
|
||||
try {
|
||||
target.close();
|
||||
}
|
||||
catch (Throwable closeEx) {
|
||||
logger.debug("Could not close JDBC Connection after failed settings", closeEx);
|
||||
}
|
||||
throw settingsEx;
|
||||
}
|
||||
|
||||
this.target = target;
|
||||
return target;
|
||||
}
|
||||
|
||||
private DataSource getDataSourceToUse() {
|
||||
|
||||
Reference in New Issue
Block a user