Skip to content

Commit a235e8a

Browse files
committed
Unwrap Parameter objects when used within PreparedOperation.
We now unwrap Parameter objects containing type and value for bind parameters when binding these from within a PreparedOperation to a statement. PreparedOperation objects are expected to use low-level R2DBC API (bind, bindNull) instead of using the Parameter abstraction. Previously, we tried to bind Parameter objects to R2DBC Statements and that has failed as drivers cannot encode Spring's Pararameter type. Closes #694
1 parent 31d4b6c commit a235e8a

File tree

5 files changed

+78
-30
lines changed

5 files changed

+78
-30
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.dialect;
17+
18+
import org.springframework.r2dbc.core.Parameter;
19+
import org.springframework.r2dbc.core.binding.BindTarget;
20+
21+
/**
22+
* Utility to bind {@link Parameter} to a {@link BindTarget}. Mainly used within the framework.
23+
*
24+
* @author Mark Paluch
25+
* @since 1.4.1
26+
*/
27+
public final class BindTargetBinder {
28+
29+
private final BindTarget target;
30+
31+
public BindTargetBinder(BindTarget target) {
32+
this.target = target;
33+
}
34+
35+
/**
36+
* Bind a {@link Parameter} by name.
37+
*
38+
* @param name must not be {@literal null}.
39+
* @param parameter must not be {@literal null}.
40+
*/
41+
public void bind(String name, Parameter parameter) {
42+
Object value = parameter.getValue();
43+
if (value == null) {
44+
target.bindNull(name, parameter.getType());
45+
} else {
46+
target.bind(name, value);
47+
}
48+
}
49+
50+
/**
51+
* Bind a {@link Parameter} by index.
52+
*
53+
* @param index must not be {@literal null}.
54+
* @param parameter must not be {@literal null}.
55+
*/
56+
public void bind(int index, Parameter parameter) {
57+
Object value = parameter.getValue();
58+
if (value == null) {
59+
target.bindNull(index, parameter.getType());
60+
} else {
61+
target.bind(index, parameter.getValue());
62+
}
63+
}
64+
}

src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionEvaluatingParameterBinder.java

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.regex.Pattern;
2424

2525
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
26+
import org.springframework.data.r2dbc.dialect.BindTargetBinder;
2627
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
2728
import org.springframework.data.repository.query.Parameter;
2829
import org.springframework.data.repository.query.Parameters;
@@ -75,12 +76,13 @@ void bind(BindTarget bindTarget,
7576
private void bindExpressions(BindTarget bindSpec,
7677
R2dbcSpELExpressionEvaluator evaluator) {
7778

79+
BindTargetBinder binder = new BindTargetBinder(bindSpec);
7880
for (ParameterBinding binding : expressionQuery.getBindings()) {
7981

8082
org.springframework.r2dbc.core.Parameter valueForBinding = getBindValue(
8183
evaluator.evaluate(binding.getExpression()));
8284

83-
bind(bindSpec, binding.getParameterName(), valueForBinding);
85+
binder.bind(binding.getParameterName(), valueForBinding);
8486
}
8587
}
8688

@@ -89,6 +91,7 @@ private void bindParameters(BindTarget bindSpec,
8991

9092
int bindingIndex = 0;
9193

94+
BindTargetBinder binder = new BindTargetBinder(bindSpec);
9295
for (Parameter bindableParameter : bindableParameters) {
9396

9497
Optional<String> name = bindableParameter.getName();
@@ -102,7 +105,7 @@ private void bindParameters(BindTarget bindSpec,
102105
org.springframework.r2dbc.core.Parameter parameter = getBindValue(values, bindableParameter);
103106

104107
if (!parameter.isEmpty() || hasBindableNullValue) {
105-
bind(bindSpec, name.get(), parameter);
108+
binder.bind(name.get(), parameter);
106109
}
107110

108111
// skip unused named parameters if there is SpEL
@@ -111,7 +114,7 @@ private void bindParameters(BindTarget bindSpec,
111114
org.springframework.r2dbc.core.Parameter parameter = getBindValue(values, bindableParameter);
112115

113116
if (!parameter.isEmpty() || hasBindableNullValue) {
114-
bind(bindSpec, bindingIndex++, parameter);
117+
binder.bind(bindingIndex++, parameter);
115118
}
116119
}
117120
}
@@ -125,27 +128,7 @@ private org.springframework.r2dbc.core.Parameter getBindValue(Object[] values, P
125128
return dataAccessStrategy.getBindValue(parameter);
126129
}
127130

128-
private static void bind(BindTarget spec, String name,
129-
org.springframework.r2dbc.core.Parameter parameter) {
130131

131-
Object value = parameter.getValue();
132-
if (value == null) {
133-
spec.bindNull(name, parameter.getType());
134-
} else {
135-
spec.bind(name, value);
136-
}
137-
}
138-
139-
private static void bind(BindTarget spec, int index,
140-
org.springframework.r2dbc.core.Parameter parameter) {
141-
142-
Object value = parameter.getValue();
143-
if (value == null) {
144-
spec.bindNull(index, parameter.getType());
145-
} else {
146-
spec.bind(index, value);
147-
}
148-
}
149132

150133
private org.springframework.r2dbc.core.Parameter getBindValue(org.springframework.r2dbc.core.Parameter bindValue) {
151134
return dataAccessStrategy.getBindValue(bindValue);

src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.data.r2dbc.convert.R2dbcConverter;
2828
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
2929
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
30+
import org.springframework.data.r2dbc.dialect.BindTargetBinder;
3031
import org.springframework.data.r2dbc.mapping.SettableValue;
3132
import org.springframework.data.r2dbc.repository.Query;
3233
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
@@ -221,10 +222,11 @@ public String getSource() {
221222
@Override
222223
public void bindTo(BindTarget target) {
223224

225+
BindTargetBinder binder = new BindTargetBinder(target);
224226
expanded.bindTo(target);
225227

226-
remainderByName.forEach(target::bind);
227-
remainderByIndex.forEach(target::bind);
228+
remainderByName.forEach(binder::bind);
229+
remainderByIndex.forEach(binder::bind);
228230
}
229231

230232
@Override

src/test/java/org/springframework/data/r2dbc/repository/PostgresR2dbcRepositoryIntegrationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ interface PostgresLegoSetRepository extends LegoSetRepository {
126126
Flux<Named> findAsProjection();
127127

128128
@Override
129-
@Query("SELECT * FROM legoset WHERE manual = :manual")
129+
@Query("SELECT * FROM legoset WHERE manual = $1")
130130
Mono<LegoSet> findByManual(int manual);
131131

132132
@Override

src/test/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQueryUnitTests.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@
5656
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
5757
import org.springframework.expression.spel.standard.SpelExpressionParser;
5858
import org.springframework.r2dbc.core.DatabaseClient;
59-
import org.springframework.r2dbc.core.Parameter;
6059
import org.springframework.r2dbc.core.PreparedOperation;
6160
import org.springframework.r2dbc.core.binding.BindTarget;
6261
import org.springframework.util.ReflectionUtils;
@@ -102,7 +101,7 @@ void bindsSimplePropertyCorrectly() {
102101
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
103102
stringQuery.bindTo(bindTarget);
104103

105-
verify(bindTarget).bind(0, Parameter.from("White"));
104+
verify(bindTarget).bind(0, "White");
106105
}
107106

108107
@Test // gh-164
@@ -116,7 +115,7 @@ void bindsPositionalPropertyCorrectly() {
116115
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
117116
stringQuery.bindTo(bindTarget);
118117

119-
verify(bindTarget).bind(0, Parameter.from("White"));
118+
verify(bindTarget).bind(0, "White");
120119
}
121120

122121
@Test
@@ -144,7 +143,7 @@ void bindsByBindmarker() {
144143
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = @lastname");
145144
stringQuery.bindTo(bindTarget);
146145

147-
verify(bindTarget).bind("lastname", Parameter.from("White"));
146+
verify(bindTarget).bind("lastname", "White");
148147
}
149148

150149
@Test

0 commit comments

Comments
 (0)