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:
committed by
Oliver Gierke
parent
a94a807bb3
commit
b09588f922
@@ -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.
|
||||
|
||||
@@ -192,4 +192,13 @@ public class ParameterBinder {
|
||||
Object[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
JpaParameters getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user