Skip to content

Commit 1e31cd6

Browse files
committedFeb 28, 2025·
Avoid getTargetConnection call on transaction-aware Connection close
Closes gh-34484 (cherry picked from commit c64dae3)
1 parent 0d60f26 commit 1e31cd6

File tree

3 files changed

+62
-11
lines changed

3 files changed

+62
-11
lines changed
 

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -439,7 +439,11 @@ private static boolean connectionEquals(ConnectionHolder conHolder, Connection p
439439
public static Connection getTargetConnection(Connection con) {
440440
Connection conToUse = con;
441441
while (conToUse instanceof ConnectionProxy connectionProxy) {
442-
conToUse = connectionProxy.getTargetConnection();
442+
Connection targetCon = connectionProxy.getTargetConnection();
443+
if (targetCon == conToUse) {
444+
break;
445+
}
446+
conToUse = targetCon;
443447
}
444448
return conToUse;
445449
}

‎spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -210,13 +210,23 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
210210
sb.append('[').append(this.target).append(']');
211211
}
212212
else {
213-
sb.append(" from DataSource [").append(this.targetDataSource).append(']');
213+
sb.append("from DataSource [").append(this.targetDataSource).append(']');
214214
}
215215
return sb.toString();
216216
}
217217
case "close" -> {
218218
// Handle close method: only close if not within a transaction.
219-
DataSourceUtils.doReleaseConnection(this.target, this.targetDataSource);
219+
if (this.target != null) {
220+
ConnectionHolder conHolder = (ConnectionHolder)
221+
TransactionSynchronizationManager.getResource(this.targetDataSource);
222+
if (conHolder != null && conHolder.hasConnection() && conHolder.getConnection() == this.target) {
223+
// It's the transactional Connection: Don't close it.
224+
conHolder.released();
225+
}
226+
else {
227+
DataSourceUtils.doCloseConnection(this.target, this.targetDataSource);
228+
}
229+
}
220230
this.closed = true;
221231
return null;
222232
}

‎spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java

+43-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ public class DataSourceTransactionManagerTests {
7272

7373
protected DataSource ds = mock();
7474

75-
protected Connection con = mock();
75+
protected ConnectionProxy con = mock();
7676

7777
protected DataSourceTransactionManager tm;
7878

@@ -81,6 +81,7 @@ public class DataSourceTransactionManagerTests {
8181
void setup() throws Exception {
8282
tm = createTransactionManager(ds);
8383
given(ds.getConnection()).willReturn(con);
84+
given(con.getTargetConnection()).willThrow(new UnsupportedOperationException());
8485
}
8586

8687
protected DataSourceTransactionManager createTransactionManager(DataSource ds) {
@@ -1074,9 +1075,9 @@ protected void doInTransactionWithoutResult(TransactionStatus status) {
10741075
Connection tCon = dsProxy.getConnection();
10751076
tCon.getWarnings();
10761077
tCon.clearWarnings();
1077-
assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
1078+
assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con);
10781079
// should be ignored
1079-
dsProxy.getConnection().close();
1080+
tCon.close();
10801081
}
10811082
catch (SQLException ex) {
10821083
throw new UncategorizedSQLException("", "", ex);
@@ -1110,9 +1111,9 @@ protected void doInTransactionWithoutResult(TransactionStatus status) {
11101111
Connection tCon = dsProxy.getConnection();
11111112
assertThatExceptionOfType(SQLException.class).isThrownBy(tCon::getWarnings);
11121113
tCon.clearWarnings();
1113-
assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
1114+
assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con);
11141115
// should be ignored
1115-
dsProxy.getConnection().close();
1116+
tCon.close();
11161117
}
11171118
catch (SQLException ex) {
11181119
throw new UncategorizedSQLException("", "", ex);
@@ -1128,6 +1129,42 @@ protected void doInTransactionWithoutResult(TransactionStatus status) {
11281129
verify(con).close();
11291130
}
11301131

1132+
@Test
1133+
void testTransactionAwareDataSourceProxyWithEarlyConnection() throws Exception {
1134+
given(ds.getConnection()).willReturn(mock(Connection.class), con);
1135+
given(con.getAutoCommit()).willReturn(true);
1136+
given(con.getWarnings()).willThrow(new SQLException());
1137+
1138+
TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
1139+
dsProxy.setLazyTransactionalConnections(false);
1140+
Connection tCon = dsProxy.getConnection();
1141+
1142+
TransactionTemplate tt = new TransactionTemplate(tm);
1143+
assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
1144+
tt.execute(new TransactionCallbackWithoutResult() {
1145+
@Override
1146+
protected void doInTransactionWithoutResult(TransactionStatus status) {
1147+
// something transactional
1148+
assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
1149+
try {
1150+
// should close the early Connection obtained before the transaction
1151+
tCon.close();
1152+
}
1153+
catch (SQLException ex) {
1154+
throw new UncategorizedSQLException("", "", ex);
1155+
}
1156+
}
1157+
});
1158+
1159+
assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
1160+
1161+
InOrder ordered = inOrder(con);
1162+
ordered.verify(con).setAutoCommit(false);
1163+
ordered.verify(con).commit();
1164+
ordered.verify(con).setAutoCommit(true);
1165+
verify(con).close();
1166+
}
1167+
11311168
@Test
11321169
void testTransactionAwareDataSourceProxyWithSuspension() throws Exception {
11331170
given(con.getAutoCommit()).willReturn(true);

0 commit comments

Comments
 (0)