DATAJPA-629 - Allow SpEL template expressions in combination with parameter expressions in @Query.

Previously SpEL template expressions like #{#entityName} could not be used in conjunction with parameter expressions in @Query because the SpEL template parser tried to evaluate the dynamic parameter expressions as well. We now mask the parameters prior to evaluating the SpEL template expression.

Original pull request: #122.
This commit is contained in:
Thomas Darimont
2014-11-14 12:44:05 +01:00
committed by Oliver Gierke
parent fa5a7ace50
commit 36a6f958f2
3 changed files with 49 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.springframework.data.jpa.repository.query;
import java.util.regex.Pattern;
import org.springframework.data.repository.core.EntityMetadata;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
@@ -35,6 +37,13 @@ import org.springframework.util.Assert;
*/
class ExpressionBasedStringQuery extends StringQuery {
private static final String EXPRESSION_PARAMETER = "?#{";
private static final String QUOTED_EXPRESSION_PARAMETER = "?__HASH__{";
private static final Pattern EXPRESSION_PARAMETER_QUOTING = Pattern.compile(Pattern.quote(EXPRESSION_PARAMETER));
private static final Pattern EXPRESSION_PARAMETER_UNQUOTING = Pattern.compile(Pattern
.quote(QUOTED_EXPRESSION_PARAMETER));
private static final String ENTITY_NAME = "entityName";
private static final String ENTITY_NAME_VARIABLE = "#" + ENTITY_NAME;
private static final String ENTITY_NAME_VARIABLE_EXPRESSION = "#{" + ENTITY_NAME_VARIABLE + "}";
@@ -70,10 +79,25 @@ class ExpressionBasedStringQuery extends StringQuery {
StandardEvaluationContext evalContext = new StandardEvaluationContext();
evalContext.setVariable(ENTITY_NAME, metadata.getEntityName());
query = potentiallyQuoteExpressionsParameter(query);
Expression expr = parser.parseExpression(query, ParserContext.TEMPLATE_EXPRESSION);
Object result = expr.getValue(evalContext, String.class);
return result == null ? query : String.valueOf(result);
String result = expr.getValue(evalContext, String.class);
if (result == null) {
return query;
}
return potentiallyUnquoteParameterExpressions(result);
}
private static String potentiallyUnquoteParameterExpressions(String result) {
return EXPRESSION_PARAMETER_UNQUOTING.matcher(result).replaceAll(EXPRESSION_PARAMETER);
}
private static String potentiallyQuoteExpressionsParameter(String query) {
return EXPRESSION_PARAMETER_QUOTING.matcher(query).replaceAll(QUOTED_EXPRESSION_PARAMETER);
}
private static boolean containsExpression(String query) {

View File

@@ -1753,6 +1753,20 @@ public class UserRepositoryTests {
assertThat(users.getContent().get(1), is(fourthUser));
}
/**
* @see DATAJPA-629
*/
@Test
public void shouldfindUsersBySpELExpressionParametersWithSpelTemplateExpression() {
flushTestUsers();
List<User> users = repository.findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntityExpression(
"Joachim", "Arrasz");
assertThat(users, hasSize(1));
assertThat(users.get(0), is(secondUser));
}
private Page<User> executeSpecWithSort(Sort sort) {
flushTestUsers();

View File

@@ -459,7 +459,7 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
*/
@Query("select u from User u where u.emailAddress = ?1")
Optional<User> findOptionalByEmailAddress(String emailAddress);
/**
* @see DATAJPA-564
*/
@@ -527,4 +527,11 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
value = "select * from (select rownum() as RN, u.* from User u) where RN between ?#{ #pageable.offset -1} and ?#{#pageable.offset + #pageable.pageSize}",
countQuery = "select count(u.id) from User u", nativeQuery = true)
Page<User> findUsersInNativeQueryWithPagination(Pageable pageable);
/**
* @see DATAJPA-629
*/
@Query("select u from #{#entityName} u where u.firstname = ?#{[0]} and u.lastname = ?#{[1]}")
List<User> findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntityExpression(String firstname,
String lastname);
}