Avoid Connection.isReadOnly() call in resetConnectionAfterTransaction

Closes gh-23747
This commit is contained in:
Juergen Hoeller
2019-10-30 00:25:17 +01:00
parent 7d02ba0694
commit 773b2f06a1
7 changed files with 107 additions and 13 deletions

View File

@@ -272,6 +272,7 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
@@ -374,7 +375,8 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
DataSourceUtils.resetConnectionAfterTransaction(
con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);

View File

@@ -169,6 +169,8 @@ public abstract class DataSourceUtils {
* @return the previous isolation level, if any
* @throws SQLException if thrown by JDBC methods
* @see #resetConnectionAfterTransaction
* @see Connection#setTransactionIsolation
* @see Connection#setReadOnly
*/
@Nullable
public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)
@@ -220,8 +222,48 @@ public abstract class DataSourceUtils {
* regarding read-only flag and isolation level.
* @param con the Connection to reset
* @param previousIsolationLevel the isolation level to restore, if any
* @param resetReadOnly whether to reset the connection's read-only flag
* @since 5.2.1
* @see #prepareConnectionForTransaction
* @see Connection#setTransactionIsolation
* @see Connection#setReadOnly
*/
public static void resetConnectionAfterTransaction(
Connection con, @Nullable Integer previousIsolationLevel, boolean resetReadOnly) {
Assert.notNull(con, "No Connection specified");
try {
// Reset transaction isolation to previous value, if changed for the transaction.
if (previousIsolationLevel != null) {
if (logger.isDebugEnabled()) {
logger.debug("Resetting isolation level of JDBC Connection [" +
con + "] to " + previousIsolationLevel);
}
con.setTransactionIsolation(previousIsolationLevel);
}
// Reset read-only flag if we originally switched it to true on transaction begin.
if (resetReadOnly) {
if (logger.isDebugEnabled()) {
logger.debug("Resetting read-only flag of JDBC Connection [" + con + "]");
}
con.setReadOnly(false);
}
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
}
/**
* Reset the given Connection after a transaction,
* regarding read-only flag and isolation level.
* @param con the Connection to reset
* @param previousIsolationLevel the isolation level to restore, if any
* @deprecated as of 5.1.11, in favor of
* {@link #resetConnectionAfterTransaction(Connection, Integer, boolean)}
*/
@Deprecated
public static void resetConnectionAfterTransaction(Connection con, @Nullable Integer previousIsolationLevel) {
Assert.notNull(con, "No Connection specified");
try {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 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.
@@ -57,35 +57,76 @@ public abstract class JdbcTransactionObjectSupport implements SavepointManager,
@Nullable
private Integer previousIsolationLevel;
private boolean readOnly = false;
private boolean savepointAllowed = false;
/**
* Set the ConnectionHolder for this transaction object.
*/
public void setConnectionHolder(@Nullable ConnectionHolder connectionHolder) {
this.connectionHolder = connectionHolder;
}
/**
* Return the ConnectionHolder for this transaction object.
*/
public ConnectionHolder getConnectionHolder() {
Assert.state(this.connectionHolder != null, "No ConnectionHolder available");
return this.connectionHolder;
}
/**
* Check whether this transaction object has a ConnectionHolder.
*/
public boolean hasConnectionHolder() {
return (this.connectionHolder != null);
}
/**
* Set the previous isolation level to retain, if any.
*/
public void setPreviousIsolationLevel(@Nullable Integer previousIsolationLevel) {
this.previousIsolationLevel = previousIsolationLevel;
}
/**
* Return the retained previous isolation level, if any.
*/
@Nullable
public Integer getPreviousIsolationLevel() {
return this.previousIsolationLevel;
}
/**
* Set the read-only status of this transaction.
* The default is {@code false}.
* @since 5.2.1
*/
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
/**
* Return the read-only status of this transaction.
* @since 5.2.1
*/
public boolean isReadOnly() {
return this.readOnly;
}
/**
* Set whether savepoints are allowed within this transaction.
* The default is {@code false}.
*/
public void setSavepointAllowed(boolean savepointAllowed) {
this.savepointAllowed = savepointAllowed;
}
/**
* Return whether savepoints are allowed within this transaction.
*/
public boolean isSavepointAllowed() {
return this.savepointAllowed;
}

View File

@@ -921,11 +921,13 @@ public class DataSourceTransactionManagerTests {
boolean condition = !TransactionSynchronizationManager.hasResource(ds);
assertThat(condition).as("Hasn't thread connection").isTrue();
InOrder ordered = inOrder(con);
ordered.verify(con).setReadOnly(true);
ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
ordered.verify(con).setAutoCommit(false);
ordered.verify(con).commit();
ordered.verify(con).setAutoCommit(true);
ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
ordered.verify(con).setReadOnly(false);
verify(con).close();
}
@@ -954,11 +956,13 @@ public class DataSourceTransactionManagerTests {
boolean condition = !TransactionSynchronizationManager.hasResource(ds);
assertThat(condition).as("Hasn't thread connection").isTrue();
InOrder ordered = inOrder(con, stmt);
ordered.verify(con).setReadOnly(true);
ordered.verify(con).setAutoCommit(false);
ordered.verify(stmt).executeUpdate("SET TRANSACTION READ ONLY");
ordered.verify(stmt).close();
ordered.verify(con).commit();
ordered.verify(con).setAutoCommit(true);
ordered.verify(con).setReadOnly(false);
ordered.verify(con).close();
}
@@ -1437,7 +1441,6 @@ public class DataSourceTransactionManagerTests {
verify(con).rollback(sp);
verify(con).releaseSavepoint(sp);
verify(con).commit();
verify(con).isReadOnly();
verify(con).close();
}
@@ -1498,7 +1501,6 @@ public class DataSourceTransactionManagerTests {
verify(con).rollback(sp);
verify(con).releaseSavepoint(sp);
verify(con).commit();
verify(con).isReadOnly();
verify(con).close();
}
@@ -1559,7 +1561,6 @@ public class DataSourceTransactionManagerTests {
verify(con).rollback(sp);
verify(con).releaseSavepoint(sp);
verify(con).commit();
verify(con).isReadOnly();
verify(con).close();
}