38
38
import java .util .List ;
39
39
import java .util .Map ;
40
40
import java .util .Set ;
41
+ import java .util .concurrent .atomic .AtomicBoolean ;
41
42
import java .util .function .BiFunction ;
42
43
import java .util .function .Function ;
43
44
import java .util .function .Supplier ;
@@ -122,15 +123,16 @@ public <T> Mono<T> inConnection(Function<Connection, Mono<T>> action) throws Dat
122
123
123
124
Assert .notNull (action , "Callback object must not be null" );
124
125
125
- Mono <Connection > connectionMono = getConnection ();
126
- // Create close-suppressing Connection proxy, also preparing returned Statements.
126
+ Mono <ConnectionCloseHolder > connectionMono = getConnection ()
127
+ . map ( it -> new ConnectionCloseHolder ( it , this :: closeConnection ));
127
128
128
129
return Mono .usingWhen (connectionMono , it -> {
129
130
130
- Connection connectionToUse = createConnectionProxy (it );
131
+ // Create close-suppressing Connection proxy
132
+ Connection connectionToUse = createConnectionProxy (it .connection );
131
133
132
134
return doInConnection (connectionToUse , action );
133
- }, this :: closeConnection , this :: closeConnection , this :: closeConnection ) //
135
+ }, ConnectionCloseHolder :: close , ConnectionCloseHolder :: close , ConnectionCloseHolder :: close ) //
134
136
.onErrorMap (R2dbcException .class , ex -> translateException ("execute" , getSql (action ), ex ));
135
137
}
136
138
@@ -149,15 +151,16 @@ public <T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action) throws
149
151
150
152
Assert .notNull (action , "Callback object must not be null" );
151
153
152
- Mono <Connection > connectionMono = getConnection ();
153
- // Create close-suppressing Connection proxy, also preparing returned Statements.
154
+ Mono <ConnectionCloseHolder > connectionMono = getConnection ()
155
+ . map ( it -> new ConnectionCloseHolder ( it , this :: closeConnection ));
154
156
155
157
return Flux .usingWhen (connectionMono , it -> {
156
158
157
- Connection connectionToUse = createConnectionProxy (it );
159
+ // Create close-suppressing Connection proxy, also preparing returned Statements.
160
+ Connection connectionToUse = createConnectionProxy (it .connection );
158
161
159
162
return doInConnectionMany (connectionToUse , action );
160
- }, this :: closeConnection , this :: closeConnection , this :: closeConnection ) //
163
+ }, ConnectionCloseHolder :: close , ConnectionCloseHolder :: close , ConnectionCloseHolder :: close ) //
161
164
.onErrorMap (R2dbcException .class , ex -> translateException ("executeMany" , getSql (action ), ex ));
162
165
}
163
166
@@ -1104,4 +1107,26 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
1104
1107
}
1105
1108
}
1106
1109
}
1110
+
1111
+ /**
1112
+ * Holder for a connection that makes sure the close action is invoked atomically only once.
1113
+ */
1114
+ @ RequiredArgsConstructor
1115
+ static class ConnectionCloseHolder extends AtomicBoolean {
1116
+
1117
+ final Connection connection ;
1118
+ final Function <Connection , Publisher <Void >> closeFunction ;
1119
+
1120
+ Mono <Void > close () {
1121
+
1122
+ return Mono .defer (() -> {
1123
+
1124
+ if (compareAndSet (false , true )) {
1125
+ return Mono .from (closeFunction .apply (connection ));
1126
+ }
1127
+
1128
+ return Mono .empty ();
1129
+ });
1130
+ }
1131
+ }
1107
1132
}
0 commit comments