DATAJPA-564 - More robust handling of SpEL expressions in String queries.

We now treat the result of the SpEL expression evaluation as Object instead of just String in ExpressionAwareParameterBinder. We now prefer index based parameter binding over named parameter binding iff only SpEL expression parameters are present. We now make named parameters available as variables in SpEL expressions.
This commit is contained in:
Thomas Darimont
2014-06-26 12:46:08 +02:00
committed by Oliver Gierke
parent a94a807bb3
commit b09588f922
5 changed files with 107 additions and 3 deletions

View File

@@ -118,7 +118,7 @@ class ExpressionAwareParameterBinder extends ParameterBinder {
* @return
*/
protected Object evaluateExpression(Expression expr) {
return expr.getValue(getEvaluationContext(), String.class);
return expr.getValue(getEvaluationContext(), Object.class);
}
/**
@@ -131,9 +131,20 @@ class ExpressionAwareParameterBinder extends ParameterBinder {
EvaluationContext delegatee = evaluationContextProvider.getEvaluationContext();
StandardEvaluationContext evalContext = new DelegatingStandardEvaluationContext(getValues(), delegatee);
populateParameterVariables(evalContext);
return evalContext;
}
private void populateParameterVariables(StandardEvaluationContext evalContext) {
for (JpaParameter param : getParameters()) {
if (param.isNamedParameter()) {
evalContext.setVariable(param.getName(), getValues()[param.getIndex()]);
}
}
}
/**
* A {@link StandardEvaluationContext} that delegates to the given {@link EvaluationContext}. Variables are first
* looked-up locally and if not the lookup is performed against the delegatee.

View File

@@ -192,4 +192,13 @@ public class ParameterBinder {
Object[] getValues() {
return values;
}
/**
* Returns the parameters.
*
* @return
*/
JpaParameters getParameters() {
return parameters;
}
}

View File

@@ -189,10 +189,18 @@ class StringQuery {
String result = query;
Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(query);
int greatestParameterIndex = determineGreatestParameterIndexIfPresent(query);
int greatestParameterIndex = tryFindGreatestParameterIndexIn(query);
boolean parametersShouldBeAccessedByIndex = greatestParameterIndex != -1;
/*
* Prefer indexed access over named parameters if only SpEL Expression parameters are present.
*/
if (!parametersShouldBeAccessedByIndex && query.contains("?#{")) {
parametersShouldBeAccessedByIndex = true;
greatestParameterIndex = 0;
}
/*
* 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
* not mix-up with the actual parameter indices.
@@ -266,7 +274,7 @@ class StringQuery {
return result;
}
private int determineGreatestParameterIndexIfPresent(String query) {
private int tryFindGreatestParameterIndexIn(String query) {
Matcher parameterIndexMatcher = PARAMETER_BINDING_BY_INDEX.matcher(query);

View File

@@ -1617,6 +1617,58 @@ public class UserRepositoryTests {
assertThat(users.get(0), is(firstUser));
}
/**
* @see DATAJPA-XXX
*/
@Test
public void shouldFindBySpELExpressionWithoutArgumentsWithQuestionmark() {
flushTestUsers();
List<User> users = repository.findOliverBySpELExpressionWithoutArgumentsWithQuestionmark();
assertThat(users, hasSize(1));
assertThat(users.get(0), is(firstUser));
}
/**
* @see DATAJPA-XXX
*/
@Test
public void shouldFindBySpELExpressionWithoutArgumentsWithColon() {
flushTestUsers();
List<User> users = repository.findOliverBySpELExpressionWithoutArgumentsWithColon();
assertThat(users, hasSize(1));
assertThat(users.get(0), is(firstUser));
}
/**
* @see DATAJPA-XXX
*/
@Test
public void shouldFindUsersByAgeForSpELExpression() {
flushTestUsers();
List<User> users = repository.findUsersByAgeForSpELExpressionByIndexedParameter(35);
assertThat(users, hasSize(1));
assertThat(users.get(0), is(secondUser));
}
/**
* @see DATAJPA-XXX
*/
@Test
public void shouldfindUsersByFirstnameForSpELExpressionWithParameterNameVariableReference() {
flushTestUsers();
List<User> users = repository.findUsersByFirstnameForSpELExpression("Joachim");
assertThat(users, hasSize(1));
assertThat(users.get(0), is(secondUser));
}
/**
* @see DATAJPA-XXX
*/

View File

@@ -477,4 +477,28 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
* @see DATAJPA-XXX
*/
List<User> queryByLastname(Expression lastname);
/**
* @see DATAJPA-XXX
*/
@Query("select u from User u where u.firstname = ?#{'Oliver'}")
List<User> findOliverBySpELExpressionWithoutArgumentsWithQuestionmark();
/**
* @see DATAJPA-XXX
*/
@Query("select u from User u where u.firstname = :#{'Oliver'}")
List<User> findOliverBySpELExpressionWithoutArgumentsWithColon();
/**
* @see DATAJPA-XXX
*/
@Query("select u from User u where u.age = ?#{[0]}")
List<User> findUsersByAgeForSpELExpressionByIndexedParameter(int age);
/**
* @see DATAJPA-XXX
*/
@Query("select u from User u where u.firstname = :firstname and u.firstname = :#{#firstname}")
List<User> findUsersByFirstnameForSpELExpression(@Param("firstname") String firstname);
}