Skip to content

Commit 782bcf3

Browse files
committed
Fine tune checks for JDBC and JPA style parameters.
The checks for JDBC and JPA parameters were sloppy and based on side effects. By using zero width lookaheads, we can precisely spot situtations where the user has both types of parameters. Otherwise, let the query on through to the JPA provider. Closes #2551.
1 parent f8d85b6 commit 782bcf3

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
* @author Mark Paluch
4848
* @author Jens Schauder
4949
* @author Diego Krupitza
50+
* @author Greg Turnquist
5051
*/
5152
class StringQuery implements DeclaredQuery {
5253

@@ -165,6 +166,10 @@ enum ParameterBindingParser {
165166
// .............................................................^ start with a question mark.
166167
private static final Pattern PARAMETER_BINDING_BY_INDEX = Pattern.compile(POSITIONAL_OR_INDEXED_PARAMETER);
167168
private static final Pattern PARAMETER_BINDING_PATTERN;
169+
private static final Pattern JDBC_STYLE_PARAM = Pattern.compile(" \\?(?!\\d)"); // <space>?[no digit]
170+
private static final Pattern NUMBERED_STYLE_PARAM = Pattern.compile(" \\?(?=\\d)"); // <space>?[digit]
171+
private static final Pattern NAMED_STYLE_PARAM = Pattern.compile(" :\\w+"); // <space>:[text]
172+
168173
private static final String MESSAGE = "Already found parameter binding with same index / parameter name but differing binding type! "
169174
+ "Already have: %s, found %s! If you bind a parameter multiple times make sure they use the same binding.";
170175
private static final int INDEXED_PARAMETER_GROUP = 4;
@@ -243,13 +248,13 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St
243248
String expression = spelExtractor.getParameter(parameterName == null ? parameterIndexString : parameterName);
244249
String replacement = null;
245250

251+
queryMeta.usesJdbcStyleParameters = JDBC_STYLE_PARAM.matcher(resultingQuery).find();
252+
usesJpaStyleParameters = NUMBERED_STYLE_PARAM.matcher(resultingQuery).find()
253+
|| NAMED_STYLE_PARAM.matcher(resultingQuery).find();
254+
246255
expressionParameterIndex++;
247256
if ("".equals(parameterIndexString)) {
248-
249-
queryMeta.usesJdbcStyleParameters = true;
250257
parameterIndex = expressionParameterIndex;
251-
} else {
252-
usesJpaStyleParameters = true;
253258
}
254259

255260
if (usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) {

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,42 @@ void noQueryShouldNotBeInvoked() {
193193
assertThatIllegalStateException().isThrownBy(() -> query.getQueryMethod());
194194
}
195195

196+
@Test // GH-2551
197+
void customQueryWithQuestionMarksShouldWork() throws NoSuchMethodException {
198+
199+
QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND,
200+
EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT);
201+
202+
Method namedMethod = UserRepository.class.getMethod("customQueryWithQuestionMarksAndNamedParam", String.class);
203+
RepositoryMetadata namedMetadata = new DefaultRepositoryMetadata(UserRepository.class);
204+
205+
strategy.resolveQuery(namedMethod, namedMetadata, projectionFactory, namedQueries);
206+
207+
assertThatIllegalArgumentException().isThrownBy(() -> {
208+
209+
Method jdbcStyleMethod = UserRepository.class.getMethod("customQueryWithQuestionMarksAndJdbcStyleParam",
210+
String.class);
211+
RepositoryMetadata jdbcStyleMetadata = new DefaultRepositoryMetadata(UserRepository.class);
212+
213+
strategy.resolveQuery(jdbcStyleMethod, jdbcStyleMetadata, projectionFactory, namedQueries);
214+
}).withMessageContaining("JDBC style parameters (?) are not supported for JPA queries.");
215+
216+
Method jpaStyleMethod = UserRepository.class.getMethod("customQueryWithQuestionMarksAndNumberedStyleParam",
217+
String.class);
218+
RepositoryMetadata jpaStyleMetadata = new DefaultRepositoryMetadata(UserRepository.class);
219+
220+
strategy.resolveQuery(jpaStyleMethod, jpaStyleMetadata, projectionFactory, namedQueries);
221+
222+
assertThatIllegalArgumentException().isThrownBy(() -> {
223+
224+
Method jpaAndJdbcStyleMethod = UserRepository.class
225+
.getMethod("customQueryWithQuestionMarksAndJdbcStyleAndNumberedStyleParam", String.class, String.class);
226+
RepositoryMetadata jpaAndJdbcMetadata = new DefaultRepositoryMetadata(UserRepository.class);
227+
228+
strategy.resolveQuery(jpaAndJdbcStyleMethod, jpaAndJdbcMetadata, projectionFactory, namedQueries);
229+
}).withMessageContaining("Mixing of ? parameters and other forms like ?1 is not supported");
230+
}
231+
196232
interface UserRepository extends Repository<User, Integer> {
197233

198234
@Query("something absurd")
@@ -210,6 +246,18 @@ interface UserRepository extends Repository<User, Integer> {
210246
@Query(value = "something absurd", name = "my-query-name")
211247
User annotatedQueryWithQueryAndQueryName();
212248

249+
@Query("SELECT * FROM table WHERE (json_col->'jsonKey')::jsonb \\?\\? :param ")
250+
List<User> customQueryWithQuestionMarksAndNamedParam(String param);
251+
252+
@Query("SELECT * FROM table WHERE (json_col->'jsonKey')::jsonb \\?\\? ? ")
253+
List<User> customQueryWithQuestionMarksAndJdbcStyleParam(String param);
254+
255+
@Query("SELECT * FROM table WHERE (json_col->'jsonKey')::jsonb \\?\\? ?1 ")
256+
List<User> customQueryWithQuestionMarksAndNumberedStyleParam(String param);
257+
258+
@Query("SELECT * FROM table WHERE (json_col->'jsonKey')::jsonb \\?\\? ?1 and other_col = ? ")
259+
List<User> customQueryWithQuestionMarksAndJdbcStyleAndNumberedStyleParam(String param1, String param2);
260+
213261
// This is a named query with Sort parameter, which isn't supported
214262
List<User> customNamedQuery(String firstname, Sort sort);
215263
}

0 commit comments

Comments
 (0)