diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java index 9edaca7857a4..82928a687639 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java @@ -67,6 +67,7 @@ * properly. Use {@link Connection#unwrap} to retrieve the native JDBC Connection. * * @author Juergen Hoeller + * @author Réda Housni Alaoui * @since 1.1 * @see javax.sql.DataSource#getConnection() * @see java.sql.Connection#close() @@ -77,6 +78,7 @@ public class TransactionAwareDataSourceProxy extends DelegatingDataSource { private boolean reobtainTransactionalConnections = false; + private boolean obtainTransactionalConnectionEagerly = false; /** @@ -107,6 +109,15 @@ public void setReobtainTransactionalConnections(boolean reobtainTransactionalCon this.reobtainTransactionalConnections = reobtainTransactionalConnections; } + /** + * Specify whether to obtain the transactional connection as soon as possible. + *
The default is "false". + *
Turning this to "true" allows to obtain the transactional connection during {@link #getConnection()} + *
This setting has no effect if {@link #reobtainTransactionalConnections} is set to "true". + */ + public void setObtainTransactionalConnectionEagerly(boolean obtainTransactionalConnectionEagerly) { + this.obtainTransactionalConnectionEagerly = obtainTransactionalConnectionEagerly; + } /** * Delegates to DataSourceUtils for automatically participating in Spring-managed @@ -130,7 +141,7 @@ public Connection getConnection() throws SQLException { * @see java.sql.Connection#close() * @see DataSourceUtils#doReleaseConnection */ - protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) { + protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) throws SQLException { return (Connection) Proxy.newProxyInstance( ConnectionProxy.class.getClassLoader(), new Class>[] {ConnectionProxy.class}, @@ -166,8 +177,11 @@ private class TransactionAwareInvocationHandler implements InvocationHandler { private boolean closed = false; - public TransactionAwareInvocationHandler(DataSource targetDataSource) { + public TransactionAwareInvocationHandler(DataSource targetDataSource) throws SQLException { this.targetDataSource = targetDataSource; + if (obtainTransactionalConnectionEagerly && shouldObtainFixedConnection(this.targetDataSource)) { + this.target = DataSourceUtils.doGetConnection(this.targetDataSource); + } } @Override 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 d8298174cfd1..f166cd945aca 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 @@ -53,6 +53,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatRuntimeException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; @@ -64,6 +65,7 @@ /** * @author Juergen Hoeller + * @author Réda Housni Alaoui * @since 04.07.2003 * @see org.springframework.jdbc.support.JdbcTransactionManagerTests */ @@ -1184,6 +1186,40 @@ protected void doInTransactionWithoutResult(TransactionStatus status) { verify(con, times(2)).close(); } + @Test + public void testTransactionAwareDataSourceProxyWithObtainTransactionalConnectionEagerly() throws SQLException { + given(con.getAutoCommit()).willReturn(true); + given(con.getWarnings()).willThrow(new SQLException()); + + TransactionTemplate tt = new TransactionTemplate(tm); + boolean condition1 = !TransactionSynchronizationManager.hasResource(ds); + assertThat(condition1).as("Hasn't thread connection").isTrue(); + tt.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + // something transactional + assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); + TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); + dsProxy.setObtainTransactionalConnectionEagerly(true); + try { + Connection connection = dsProxy.getConnection(); + assertThatThrownBy(connection::getWarnings).isInstanceOf(SQLException.class); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); + } + } + }); + + boolean condition = !TransactionSynchronizationManager.hasResource(ds); + assertThat(condition).as("Hasn't thread connection").isTrue(); + InOrder ordered = inOrder(con); + ordered.verify(con).setAutoCommit(false); + ordered.verify(con).commit(); + ordered.verify(con).setAutoCommit(true); + verify(con).close(); + } + /** * Test behavior if the first operation on a connection (getAutoCommit) throws SQLException. */ diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java index a3deb9a1a9b5..9534498e3ea8 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java @@ -59,6 +59,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatRuntimeException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; @@ -70,6 +71,7 @@ /** * @author Juergen Hoeller + * @author Réda Housni Alaoui * @since 5.3 * @see org.springframework.jdbc.datasource.DataSourceTransactionManagerTests */ @@ -1112,6 +1114,40 @@ protected void doInTransactionWithoutResult(TransactionStatus status) { verify(con, times(2)).close(); } + @Test + public void testTransactionAwareDataSourceProxyWithObtainTransactionalConnectionEagerly() throws SQLException { + given(con.getAutoCommit()).willReturn(true); + given(con.getWarnings()).willThrow(new SQLException()); + + TransactionTemplate tt = new TransactionTemplate(tm); + boolean condition1 = !TransactionSynchronizationManager.hasResource(ds); + assertThat(condition1).as("Hasn't thread connection").isTrue(); + tt.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + // something transactional + assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con); + TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds); + dsProxy.setObtainTransactionalConnectionEagerly(true); + try { + Connection connection = dsProxy.getConnection(); + assertThatThrownBy(connection::getWarnings).isInstanceOf(SQLException.class); + } + catch (SQLException ex) { + throw new UncategorizedSQLException("", "", ex); + } + } + }); + + boolean condition = !TransactionSynchronizationManager.hasResource(ds); + assertThat(condition).as("Hasn't thread connection").isTrue(); + InOrder ordered = inOrder(con); + ordered.verify(con).setAutoCommit(false); + ordered.verify(con).commit(); + ordered.verify(con).setAutoCommit(true); + verify(con).close(); + } + /** * Test behavior if the first operation on a connection (getAutoCommit) throws SQLException. */