2929import org .springframework .data .jdbc .core .convert .JdbcColumnTypes ;
3030import org .springframework .data .jdbc .core .convert .JdbcConverter ;
3131import org .springframework .data .jdbc .core .mapping .JdbcValue ;
32+ import org .springframework .data .jdbc .repository .query .parameter .ParameterBindingParser ;
33+ import org .springframework .data .jdbc .repository .query .parameter .ParameterBindings .Metadata ;
34+ import org .springframework .data .jdbc .repository .query .parameter .ParameterBindings .ParameterBinding ;
3235import org .springframework .data .jdbc .support .JdbcUtil ;
3336import org .springframework .data .relational .core .mapping .RelationalMappingContext ;
3437import org .springframework .data .relational .repository .query .RelationalParameterAccessor ;
3538import org .springframework .data .relational .repository .query .RelationalParameters ;
3639import org .springframework .data .relational .repository .query .RelationalParametersParameterAccessor ;
3740import org .springframework .data .repository .query .Parameter ;
3841import org .springframework .data .repository .query .Parameters ;
42+ import org .springframework .data .repository .query .QueryMethodEvaluationContextProvider ;
3943import org .springframework .data .repository .query .ResultProcessor ;
44+ import org .springframework .data .repository .query .SpelQueryContext ;
45+ import org .springframework .expression .EvaluationContext ;
46+ import org .springframework .expression .Expression ;
47+ import org .springframework .expression .ExpressionParser ;
48+ import org .springframework .expression .spel .standard .SpelExpressionParser ;
4049import org .springframework .jdbc .core .ResultSetExtractor ;
4150import org .springframework .jdbc .core .RowMapper ;
4251import org .springframework .jdbc .core .namedparam .MapSqlParameterSource ;
4352import org .springframework .jdbc .core .namedparam .NamedParameterJdbcOperations ;
53+ import org .springframework .jdbc .core .namedparam .SqlParameterSource ;
4454import org .springframework .lang .Nullable ;
4555import org .springframework .util .Assert ;
4656import org .springframework .util .ClassUtils ;
5767 * @author Mark Paluch
5868 * @author Hebert Coelho
5969 * @author Chirag Tailor
70+ * @author Christopher Klein
6071 * @since 2.0
6172 */
6273public class StringBasedJdbcQuery extends AbstractJdbcQuery {
@@ -67,6 +78,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
6778 private final JdbcConverter converter ;
6879 private final RowMapperFactory rowMapperFactory ;
6980 private BeanFactory beanFactory ;
81+ private final QueryMethodEvaluationContextProvider evaluationContextProvider ;
7082
7183 /**
7284 * Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
@@ -77,8 +89,9 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
7789 * @param defaultRowMapper can be {@literal null} (only in case of a modifying query).
7890 */
7991 public StringBasedJdbcQuery (JdbcQueryMethod queryMethod , NamedParameterJdbcOperations operations ,
80- @ Nullable RowMapper <?> defaultRowMapper , JdbcConverter converter ) {
81- this (queryMethod , operations , result -> (RowMapper <Object >) defaultRowMapper , converter );
92+ @ Nullable RowMapper <?> defaultRowMapper , JdbcConverter converter ,
93+ QueryMethodEvaluationContextProvider evaluationContextProvider ) {
94+ this (queryMethod , operations , result -> (RowMapper <Object >) defaultRowMapper , converter , evaluationContextProvider );
8295 }
8396
8497 /**
@@ -91,7 +104,8 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
91104 * @since 2.3
92105 */
93106 public StringBasedJdbcQuery (JdbcQueryMethod queryMethod , NamedParameterJdbcOperations operations ,
94- RowMapperFactory rowMapperFactory , JdbcConverter converter ) {
107+ RowMapperFactory rowMapperFactory , JdbcConverter converter ,
108+ QueryMethodEvaluationContextProvider evaluationContextProvider ) {
95109
96110 super (queryMethod , operations );
97111
@@ -100,6 +114,7 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
100114 this .queryMethod = queryMethod ;
101115 this .converter = converter ;
102116 this .rowMapperFactory = rowMapperFactory ;
117+ this .evaluationContextProvider = evaluationContextProvider ;
103118
104119 if (queryMethod .isSliceQuery ()) {
105120 throw new UnsupportedOperationException (
@@ -115,6 +130,16 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
115130 @ Override
116131 public Object execute (Object [] objects ) {
117132
133+ // List<ParameterBinding> parameterBindings = new ArrayList<>();
134+ // SpelQueryContext queryContext = SpelQueryContext.of((counter, expression) -> {
135+ //
136+ // String parameterName = String.format("__synthetic_%d__", counter);
137+ // parameterBindings.add(new ParameterBinding(parameterName, expression));
138+ // return parameterName;
139+ // }, String::concat);
140+ //
141+ // SpelQueryContext.SpelExtractor parsed = queryContext.parse(query);
142+
118143 RelationalParameterAccessor accessor = new RelationalParametersParameterAccessor (getQueryMethod (), objects );
119144 ResultProcessor processor = getQueryMethod ().getResultProcessor ().withDynamicProjection (accessor );
120145 ResultProcessingConverter converter = new ResultProcessingConverter (processor , this .converter .getMappingContext (),
@@ -128,7 +153,51 @@ public Object execute(Object[] objects) {
128153 determineResultSetExtractor (rowMapper ), //
129154 rowMapper );
130155
131- return queryExecution .execute (determineQuery (), this .bindParameters (accessor ));
156+ Metadata queryMeta = new Metadata ();
157+
158+ String query = determineQuery ();
159+
160+ if (ObjectUtils .isEmpty (query )) {
161+ throw new IllegalStateException (String .format ("No query specified on %s" , queryMethod .getName ()));
162+ }
163+ List <ParameterBinding > bindings = new ArrayList <>();
164+
165+ query = ParameterBindingParser .INSTANCE .parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery (query ,
166+ bindings , queryMeta );
167+
168+ SqlParameterSource parameterMap = this .bindParameters (accessor );
169+ extendParametersFromSpELEvaluation ((MapSqlParameterSource ) parameterMap , bindings , objects );
170+ return queryExecution .execute (query , parameterMap );
171+ }
172+
173+ /**
174+ * Extend the {@link MapSqlParameterSource} by evaluating each detected SpEL parameter in the original query. This is
175+ * basically a simple variant of Spring Data JPA's SPeL implementation.
176+ *
177+ * @param parameterMap
178+ * @param bindings
179+ * @param values
180+ */
181+ void extendParametersFromSpELEvaluation (MapSqlParameterSource parameterMap , List <ParameterBinding > bindings ,
182+ Object [] values ) {
183+
184+ if (bindings .size () == 0 ) {
185+ return ;
186+ }
187+
188+ ExpressionParser parser = new SpelExpressionParser ();
189+
190+ bindings .forEach (binding -> {
191+ if (!binding .isExpression ()) {
192+ return ;
193+ }
194+
195+ Expression expression = parser .parseExpression (binding .getExpression ());
196+ EvaluationContext context = evaluationContextProvider .getEvaluationContext (this .queryMethod .getParameters (),
197+ values );
198+
199+ parameterMap .addValue (binding .getName (), expression .getValue (context , Object .class ));
200+ });
132201 }
133202
134203 @ Override
0 commit comments