diff --git a/pom.xml b/pom.xml
index 335f5969b8..5455f1f684 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-GH-1904-SNAPSHOT
pom
Spring Data Relational Parent
@@ -20,7 +20,7 @@
spring-data-jdbc
- 3.4.0-SNAPSHOT
+ 3.4.0-GH-3049-SNAPSHOT
4.21.1
reuseReports
diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml
index 84eeb178e0..e764e4e143 100644
--- a/spring-data-jdbc-distribution/pom.xml
+++ b/spring-data-jdbc-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-GH-1904-SNAPSHOT
../pom.xml
diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml
index 266af71b96..72e2bc74ac 100644
--- a/spring-data-jdbc/pom.xml
+++ b/spring-data-jdbc/pom.xml
@@ -6,7 +6,7 @@
4.0.0
spring-data-jdbc
- 3.4.0-SNAPSHOT
+ 3.4.0-GH-1904-SNAPSHOT
Spring Data JDBC
Spring Data module for JDBC repositories.
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-GH-1904-SNAPSHOT
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
index 3e8aebc44d..dd8e4a35f7 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
@@ -30,6 +30,9 @@
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
+import org.springframework.core.env.StandardEnvironment;
+import org.springframework.data.expression.ValueEvaluationContext;
+import org.springframework.data.expression.ValueExpressionParser;
import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.mapping.JdbcValue;
@@ -37,12 +40,14 @@
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
+import org.springframework.data.repository.query.CachingValueExpressionDelegate;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
+import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
import org.springframework.data.repository.query.ResultProcessor;
-import org.springframework.data.repository.query.SpelEvaluator;
-import org.springframework.data.repository.query.SpelQueryContext;
+import org.springframework.data.repository.query.ValueExpressionDelegate;
+import org.springframework.data.repository.query.ValueExpressionQueryRewriter;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.jdbc.core.ResultSetExtractor;
@@ -67,6 +72,7 @@
* @author Chirag Tailor
* @author Christopher Klein
* @author Mikhail Polivakha
+ * @author Marcin Grzejszczak
* @since 2.0
*/
public class StringBasedJdbcQuery extends AbstractJdbcQuery {
@@ -74,12 +80,12 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
private static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to provide names for method parameters; Use @Param for query method parameters, or use the javac flag -parameters";
private final JdbcConverter converter;
private final RowMapperFactory rowMapperFactory;
- private final SpelEvaluator spelEvaluator;
- private final boolean containsSpelExpressions;
+ private final ValueExpressionQueryRewriter.ParsedQuery parsedQuery;
private final String query;
private final CachedRowMapperFactory cachedRowMapperFactory;
private final CachedResultSetExtractorFactory cachedResultSetExtractorFactory;
+ private final ValueExpressionDelegate delegate;
/**
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
@@ -88,7 +94,9 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param defaultRowMapper can be {@literal null} (only in case of a modifying query).
+ * @deprecated since 3.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
*/
+ @Deprecated(since = "3.4")
public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
@Nullable RowMapper> defaultRowMapper, JdbcConverter converter,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
@@ -115,6 +123,23 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
evaluationContextProvider);
}
+ /**
+ * Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
+ * and {@link RowMapperFactory}.
+ *
+ * @param queryMethod must not be {@literal null}.
+ * @param operations must not be {@literal null}.
+ * @param rowMapperFactory must not be {@literal null}.
+ * @param converter must not be {@literal null}.
+ * @param delegate must not be {@literal null}.
+ * @since 3.4
+ */
+ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
+ RowMapperFactory rowMapperFactory, JdbcConverter converter,
+ ValueExpressionDelegate delegate) {
+ this(queryMethod.getRequiredQuery(), queryMethod, operations, rowMapperFactory, converter, delegate);
+ }
+
/**
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
* and {@link RowMapperFactory}.
@@ -124,15 +149,13 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
* @param operations must not be {@literal null}.
* @param rowMapperFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
- * @param evaluationContextProvider must not be {@literal null}.
+ * @param delegate must not be {@literal null}.
* @since 3.4
*/
public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
RowMapperFactory rowMapperFactory, JdbcConverter converter,
- QueryMethodEvaluationContextProvider evaluationContextProvider) {
-
+ ValueExpressionDelegate delegate) {
super(queryMethod, operations);
-
Assert.hasText(query, "Query must not be null or empty");
Assert.notNull(rowMapperFactory, "RowMapperFactory must not be null");
@@ -159,13 +182,35 @@ public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedPara
this.cachedResultSetExtractorFactory = new CachedResultSetExtractorFactory(
this.cachedRowMapperFactory::getRowMapper);
- SpelQueryContext.EvaluatingSpelQueryContext queryContext = SpelQueryContext
- .of((counter, expression) -> String.format("__$synthetic$__%d", counter + 1), String::concat)
- .withEvaluationContextProvider(evaluationContextProvider);
+ ValueExpressionQueryRewriter rewriter = ValueExpressionQueryRewriter.of(delegate,
+ (counter, expression) -> String.format("__$synthetic$__%d", counter + 1), String::concat);
this.query = query;
- this.spelEvaluator = queryContext.parse(this.query, getQueryMethod().getParameters());
- this.containsSpelExpressions = !this.spelEvaluator.getQueryString().equals(this.query);
+ this.parsedQuery = rewriter.parse(this.query);
+ this.delegate = delegate;
+ }
+
+ /**
+ * Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
+ * and {@link RowMapperFactory}.
+ *
+ * @param query must not be {@literal null} or empty.
+ * @param queryMethod must not be {@literal null}.
+ * @param operations must not be {@literal null}.
+ * @param rowMapperFactory must not be {@literal null}.
+ * @param converter must not be {@literal null}.
+ * @param evaluationContextProvider must not be {@literal null}.
+ * @since 3.4
+ * @deprecated since 3.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
+ */
+ @Deprecated(since = "3.4")
+ public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
+ RowMapperFactory rowMapperFactory, JdbcConverter converter,
+ QueryMethodEvaluationContextProvider evaluationContextProvider) {
+ this(query, queryMethod, operations, rowMapperFactory, converter, new CachingValueExpressionDelegate(
+ new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), rootObject -> evaluationContextProvider
+ .getEvaluationContext(queryMethod.getParameters(), new Object[] { rootObject })),
+ ValueExpressionParser.create()));
}
@Override
@@ -177,15 +222,23 @@ public Object execute(Object[] objects) {
JdbcQueryExecution> queryExecution = createJdbcQueryExecution(accessor, processor);
MapSqlParameterSource parameterMap = this.bindParameters(accessor);
- return queryExecution.execute(processSpelExpressions(objects, parameterMap), parameterMap);
+ return queryExecution.execute(evaluateExpressions(objects, accessor.getBindableParameters(), parameterMap),
+ parameterMap);
}
- private String processSpelExpressions(Object[] objects, MapSqlParameterSource parameterMap) {
+ private String evaluateExpressions(Object[] objects, Parameters, ?> bindableParameters,
+ MapSqlParameterSource parameterMap) {
- if (containsSpelExpressions) {
+ if (parsedQuery.hasParameterBindings()) {
- spelEvaluator.evaluate(objects).forEach(parameterMap::addValue);
- return spelEvaluator.getQueryString();
+ ValueEvaluationContext evaluationContext = delegate.createValueContextProvider(bindableParameters)
+ .getEvaluationContext(objects);
+
+ parsedQuery.getParameterMap().forEach((paramName, valueExpression) -> {
+ parameterMap.addValue(paramName, valueExpression.evaluate(evaluationContext));
+ });
+
+ return parsedQuery.getQueryString();
}
return this.query;
@@ -196,13 +249,12 @@ private JdbcQueryExecution> createJdbcQueryExecution(RelationalParameterAccess
if (getQueryMethod().isModifyingQuery()) {
return createModifyingQueryExecutor();
- } else {
+ }
- Supplier> rowMapper = () -> determineRowMapper(processor, accessor.findDynamicProjection() != null);
- ResultSetExtractor