Skip to content

Commit 0bf24ad

Browse files
committed
DATAJPA-1267 - Rebuilding DATAJPA-1307.
1 parent 026c546 commit 0bf24ad

File tree

1 file changed

+65
-22
lines changed

1 file changed

+65
-22
lines changed

src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java

+65-22
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class StringQuery implements DeclaredQuery {
5454
private final @Nullable String alias;
5555
private final boolean hasConstructorExpression;
5656
private final boolean containsPageableInSpel;
57+
private final boolean usesJdbcStyleParameters;
5758

5859
/**
5960
* Creates a new {@link StringQuery} from the given JPQL query.
@@ -67,9 +68,12 @@ class StringQuery implements DeclaredQuery {
6768

6869
this.bindings = new ArrayList<>();
6970
this.containsPageableInSpel = query.contains("#pageable");
71+
72+
Metadata queryMeta = new Metadata();
7073
this.query = ParameterBindingParser.INSTANCE.parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(query,
71-
this.bindings);
74+
this.bindings, queryMeta);
7275

76+
this.usesJdbcStyleParameters = queryMeta.usesJdbcStyleParameters;
7377
this.alias = QueryUtils.detectAlias(query);
7478
this.hasConstructorExpression = QueryUtils.hasConstructorExpression(query);
7579
}
@@ -112,7 +116,7 @@ public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable Str
112116
*/
113117
@Override
114118
public boolean usesJdbcStyleParameters() {
115-
return false;
119+
return usesJdbcStyleParameters;
116120
}
117121

118122
/*
@@ -180,7 +184,11 @@ enum ParameterBindingParser {
180184
INSTANCE;
181185

182186
private static final String EXPRESSION_PARAMETER_PREFIX = "__$synthetic$__";
183-
private static final Pattern PARAMETER_BINDING_BY_INDEX = Pattern.compile("\\?(\\d+)");
187+
public static final String POSITIONAL_OR_INDEXED_PARAMETER = "\\?(\\d*+(?![#\\w]))";
188+
// .....................................................................^ not followed by a hash or a letter.
189+
// .................................................................^ zero or more digits.
190+
// .............................................................^ start with a question mark.
191+
private static final Pattern PARAMETER_BINDING_BY_INDEX = Pattern.compile(POSITIONAL_OR_INDEXED_PARAMETER);
184192
private static final Pattern PARAMETER_BINDING_PATTERN;
185193
private static final String MESSAGE = "Already found parameter binding with same index / parameter name but differing binding type! "
186194
+ "Already have: %s, found %s! If you bind a parameter multiple times make sure they use the same binding.";
@@ -205,7 +213,7 @@ enum ParameterBindingParser {
205213
builder.append("(?: )?"); // some whitespace
206214
builder.append("\\(?"); // optional braces around parameters
207215
builder.append("(");
208-
builder.append("%?(\\?(\\d+))%?"); // position parameter and parameter index
216+
builder.append("%?(" + POSITIONAL_OR_INDEXED_PARAMETER + ")%?"); // position parameter and parameter index
209217
builder.append("|"); // or
210218

211219
// named parameter and the parameter name
@@ -222,13 +230,29 @@ enum ParameterBindingParser {
222230
* the cleaned up query.
223231
*/
224232
private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(String query,
225-
List<ParameterBinding> bindings) {
233+
List<ParameterBinding> bindings, Metadata queryMeta) {
234+
235+
int greatestParameterIndex = tryFindGreatestParameterIndexIn(query);
226236

227-
SpelExtractor spelExtractor = createSpelExtractor(query);
237+
boolean parametersShouldBeAccessedByIndex = greatestParameterIndex != -1;
238+
239+
/*
240+
* Prefer indexed access over named parameters if only SpEL Expression parameters are present.
241+
*/
242+
if (!parametersShouldBeAccessedByIndex && query.contains("?#{")) {
243+
parametersShouldBeAccessedByIndex = true;
244+
greatestParameterIndex = 0;
245+
}
246+
247+
SpelExtractor spelExtractor = createSpelExtractor(query, parametersShouldBeAccessedByIndex,
248+
greatestParameterIndex);
228249

229250
String resultingQuery = spelExtractor.getQueryString();
230251
Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(resultingQuery);
231252

253+
int expressionParameterIndex = parametersShouldBeAccessedByIndex ? greatestParameterIndex : 0;
254+
255+
boolean usesJpaStyleParameters = false;
232256
while (matcher.find()) {
233257

234258
if (spelExtractor.isQuoted(matcher.start())) {
@@ -237,12 +261,26 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St
237261

238262
String parameterIndexString = matcher.group(INDEXED_PARAMETER_GROUP);
239263
String parameterName = parameterIndexString != null ? null : matcher.group(NAMED_PARAMETER_GROUP);
240-
Integer parameterIndex = parameterIndexString == null ? null : Integer.valueOf(parameterIndexString);
264+
Integer parameterIndex = getParameterIndex(parameterIndexString);
265+
241266
String typeSource = matcher.group(COMPARISION_TYPE_GROUP);
242267
String expression = spelExtractor.getParameter(parameterName == null ? parameterIndexString : parameterName);
243268
String replacement = null;
244269

245-
Assert.isTrue(parameterIndex != null || parameterName != null, "We need either a name or an index.");
270+
Assert.isTrue(parameterIndexString != null || parameterName != null, "We need either a name or an index.");
271+
272+
expressionParameterIndex++;
273+
if ("".equals(parameterIndexString)) {
274+
275+
queryMeta.usesJdbcStyleParameters = true;
276+
parameterIndex = expressionParameterIndex;
277+
} else {
278+
usesJpaStyleParameters = true;
279+
}
280+
281+
if (usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) {
282+
throw new IllegalArgumentException("Mixing of ? parameters and other forms like ?1 is not supported");
283+
}
246284

247285
switch (ParameterBindingType.of(typeSource)) {
248286

@@ -287,19 +325,8 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St
287325
return resultingQuery;
288326
}
289327

290-
private SpelExtractor createSpelExtractor(String queryWithSpel) {
291-
292-
int greatestParameterIndex = tryFindGreatestParameterIndexIn(queryWithSpel);
293-
294-
boolean parametersShouldBeAccessedByIndex = greatestParameterIndex != -1;
295-
296-
/*
297-
* Prefer indexed access over named parameters if only SpEL Expression parameters are present.
298-
*/
299-
if (!parametersShouldBeAccessedByIndex && queryWithSpel.contains("?#{")) {
300-
parametersShouldBeAccessedByIndex = true;
301-
greatestParameterIndex = 0;
302-
}
328+
private SpelExtractor createSpelExtractor(String queryWithSpel, boolean parametersShouldBeAccessedByIndex,
329+
int greatestParameterIndex) {
303330

304331
/*
305332
* If parameters need to be bound by index, we bind the synthetic expression parameters starting from position of the greatest discovered index parameter in order to
@@ -328,14 +355,27 @@ private String replaceFirst(String text, String substring, String replacement) {
328355
return text.substring(0, index) + replacement + text.substring(index + substring.length());
329356
}
330357

358+
@Nullable
359+
private Integer getParameterIndex(@Nullable String parameterIndexString) {
360+
361+
if (parameterIndexString == null || parameterIndexString.isEmpty()) {
362+
return null;
363+
}
364+
return Integer.valueOf(parameterIndexString);
365+
}
366+
331367
private int tryFindGreatestParameterIndexIn(String query) {
332368

333369
Matcher parameterIndexMatcher = PARAMETER_BINDING_BY_INDEX.matcher(query);
334370

335371
int greatestParameterIndex = -1;
336372
while (parameterIndexMatcher.find()) {
373+
337374
String parameterIndexString = parameterIndexMatcher.group(1);
338-
greatestParameterIndex = Math.max(greatestParameterIndex, Integer.parseInt(parameterIndexString));
375+
Integer parameterIndex = getParameterIndex(parameterIndexString);
376+
if (parameterIndex != null) {
377+
greatestParameterIndex = Math.max(greatestParameterIndex, parameterIndex);
378+
}
339379
}
340380

341381
return greatestParameterIndex;
@@ -790,4 +830,7 @@ private static Type getLikeTypeFrom(String expression) {
790830
}
791831
}
792832

833+
static class Metadata {
834+
private boolean usesJdbcStyleParameters = false;
835+
}
793836
}

0 commit comments

Comments
 (0)