20
20
import java .lang .reflect .Array ;
21
21
import java .lang .reflect .Constructor ;
22
22
import java .sql .SQLType ;
23
+ import java .util .AbstractMap ;
23
24
import java .util .ArrayList ;
24
25
import java .util .Collection ;
25
26
import java .util .LinkedHashMap ;
26
27
import java .util .List ;
28
+ import java .util .Map ;
27
29
import java .util .function .Function ;
28
30
import java .util .function .Supplier ;
29
31
30
32
import org .springframework .beans .BeanInstantiationException ;
31
33
import org .springframework .beans .BeanUtils ;
32
34
import org .springframework .beans .factory .BeanFactory ;
35
+ import org .springframework .data .expression .ValueEvaluationContext ;
36
+ import org .springframework .data .expression .ValueExpressionParser ;
33
37
import org .springframework .data .jdbc .core .convert .JdbcColumnTypes ;
34
38
import org .springframework .data .jdbc .core .convert .JdbcConverter ;
35
39
import org .springframework .data .jdbc .core .mapping .JdbcValue ;
36
40
import org .springframework .data .jdbc .support .JdbcUtil ;
37
41
import org .springframework .data .relational .core .mapping .RelationalMappingContext ;
38
42
import org .springframework .data .relational .repository .query .RelationalParameterAccessor ;
39
43
import org .springframework .data .relational .repository .query .RelationalParametersParameterAccessor ;
44
+ import org .springframework .data .repository .query .CachingValueExpressionDelegate ;
40
45
import org .springframework .data .repository .query .Parameter ;
41
46
import org .springframework .data .repository .query .Parameters ;
42
47
import org .springframework .data .repository .query .QueryMethodEvaluationContextProvider ;
48
+ import org .springframework .data .repository .query .QueryMethodValueEvaluationContextAccessor ;
43
49
import org .springframework .data .repository .query .ResultProcessor ;
44
- import org .springframework .data .repository .query .SpelEvaluator ;
45
- import org .springframework .data .repository .query .SpelQueryContext ;
50
+ import org .springframework .data .repository .query .ValueExpressionDelegate ;
51
+ import org .springframework .data .repository .query .ValueExpressionQueryRewriter ;
46
52
import org .springframework .data .util .Lazy ;
47
53
import org .springframework .data .util .TypeInformation ;
54
+ import org .springframework .expression .spel .standard .SpelExpressionParser ;
48
55
import org .springframework .jdbc .core .ResultSetExtractor ;
49
56
import org .springframework .jdbc .core .RowMapper ;
50
57
import org .springframework .jdbc .core .namedparam .MapSqlParameterSource ;
@@ -74,12 +81,14 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
74
81
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" ;
75
82
private final JdbcConverter converter ;
76
83
private final RowMapperFactory rowMapperFactory ;
77
- private final SpelEvaluator spelEvaluator ;
84
+ private final ValueExpressionQueryRewriter . ParsedQuery parsedQuery ;
78
85
private final boolean containsSpelExpressions ;
79
86
private final String query ;
80
87
81
88
private final CachedRowMapperFactory cachedRowMapperFactory ;
82
89
private final CachedResultSetExtractorFactory cachedResultSetExtractorFactory ;
90
+ private final ValueExpressionDelegate delegate ;
91
+ private final List <Map .Entry <String , String >> parameterBindings ;
83
92
84
93
/**
85
94
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
@@ -88,7 +97,9 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
88
97
* @param queryMethod must not be {@literal null}.
89
98
* @param operations must not be {@literal null}.
90
99
* @param defaultRowMapper can be {@literal null} (only in case of a modifying query).
100
+ * @deprecated since 3.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
91
101
*/
102
+ @ Deprecated (since = "3.4" )
92
103
public StringBasedJdbcQuery (JdbcQueryMethod queryMethod , NamedParameterJdbcOperations operations ,
93
104
@ Nullable RowMapper <?> defaultRowMapper , JdbcConverter converter ,
94
105
QueryMethodEvaluationContextProvider evaluationContextProvider ) {
@@ -116,6 +127,23 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
116
127
evaluationContextProvider );
117
128
}
118
129
130
+ /**
131
+ * Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
132
+ * and {@link RowMapperFactory}.
133
+ *
134
+ * @param queryMethod must not be {@literal null}.
135
+ * @param operations must not be {@literal null}.
136
+ * @param rowMapperFactory must not be {@literal null}.
137
+ * @param converter must not be {@literal null}.
138
+ * @param delegate must not be {@literal null}.
139
+ * @since 3.4
140
+ */
141
+ public StringBasedJdbcQuery (JdbcQueryMethod queryMethod , NamedParameterJdbcOperations operations ,
142
+ RowMapperFactory rowMapperFactory , JdbcConverter converter ,
143
+ ValueExpressionDelegate delegate ) {
144
+ this (queryMethod .getRequiredQuery (), queryMethod , operations , rowMapperFactory , converter , delegate );
145
+ }
146
+
119
147
/**
120
148
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
121
149
* and {@link RowMapperFactory}.
@@ -125,15 +153,13 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
125
153
* @param operations must not be {@literal null}.
126
154
* @param rowMapperFactory must not be {@literal null}.
127
155
* @param converter must not be {@literal null}.
128
- * @param evaluationContextProvider must not be {@literal null}.
156
+ * @param delegate must not be {@literal null}.
129
157
* @since 3.4
130
158
*/
131
159
public StringBasedJdbcQuery (String query , JdbcQueryMethod queryMethod , NamedParameterJdbcOperations operations ,
132
160
RowMapperFactory rowMapperFactory , JdbcConverter converter ,
133
- QueryMethodEvaluationContextProvider evaluationContextProvider ) {
134
-
161
+ ValueExpressionDelegate delegate ) {
135
162
super (queryMethod , operations );
136
-
137
163
Assert .hasText (query , "Query must not be null or empty" );
138
164
Assert .notNull (rowMapperFactory , "RowMapperFactory must not be null" );
139
165
@@ -160,13 +186,40 @@ public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedPara
160
186
this .cachedResultSetExtractorFactory = new CachedResultSetExtractorFactory (
161
187
this .cachedRowMapperFactory ::getRowMapper );
162
188
163
- SpelQueryContext .EvaluatingSpelQueryContext queryContext = SpelQueryContext
164
- .of ((counter , expression ) -> String .format ("__$synthetic$__%d" , counter + 1 ), String ::concat )
165
- .withEvaluationContextProvider (evaluationContextProvider );
189
+ this .parameterBindings = new ArrayList <>();
190
+
191
+ ValueExpressionQueryRewriter rewriter = ValueExpressionQueryRewriter .of (delegate , (counter , expression ) -> {
192
+ String newName = String .format ("__$synthetic$__%d" , counter + 1 );
193
+ parameterBindings .add (new AbstractMap .SimpleEntry <>(newName , expression ));
194
+ return newName ;
195
+ }, String ::concat );
166
196
167
197
this .query = query ;
168
- this .spelEvaluator = queryContext .parse (this .query , getQueryMethod ().getParameters ());
169
- this .containsSpelExpressions = !this .spelEvaluator .getQueryString ().equals (this .query );
198
+ this .parsedQuery = rewriter .parse (this .query );
199
+ this .containsSpelExpressions = !this .parsedQuery .getQueryString ().equals (this .query );
200
+ this .delegate = delegate ;
201
+ }
202
+
203
+ /**
204
+ * Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
205
+ * and {@link RowMapperFactory}.
206
+ *
207
+ * @param query must not be {@literal null} or empty.
208
+ * @param queryMethod must not be {@literal null}.
209
+ * @param operations must not be {@literal null}.
210
+ * @param rowMapperFactory must not be {@literal null}.
211
+ * @param converter must not be {@literal null}.
212
+ * @param evaluationContextProvider must not be {@literal null}.
213
+ * @since 3.4
214
+ * @deprecated since 3.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
215
+ */
216
+ @ Deprecated (since = "3.4" )
217
+ public StringBasedJdbcQuery (String query , JdbcQueryMethod queryMethod , NamedParameterJdbcOperations operations ,
218
+ RowMapperFactory rowMapperFactory , JdbcConverter converter ,
219
+ QueryMethodEvaluationContextProvider evaluationContextProvider ) {
220
+ this (query , queryMethod , operations , rowMapperFactory , converter , new CachingValueExpressionDelegate (new QueryMethodValueEvaluationContextAccessor (null ,
221
+ rootObject -> evaluationContextProvider .getEvaluationContext (queryMethod .getParameters (), new Object [] { rootObject })), ValueExpressionParser .create (
222
+ SpelExpressionParser ::new )));
170
223
}
171
224
172
225
@ Override
@@ -178,15 +231,19 @@ public Object execute(Object[] objects) {
178
231
JdbcQueryExecution <?> queryExecution = createJdbcQueryExecution (accessor , processor );
179
232
MapSqlParameterSource parameterMap = this .bindParameters (accessor );
180
233
181
- return queryExecution .execute (processSpelExpressions (objects , parameterMap ), parameterMap );
234
+ return queryExecution .execute (processSpelExpressions (objects , accessor . getBindableParameters (), parameterMap ), parameterMap );
182
235
}
183
236
184
- private String processSpelExpressions (Object [] objects , MapSqlParameterSource parameterMap ) {
237
+ private String processSpelExpressions (Object [] objects , Parameters <?, ?> bindableParameters , MapSqlParameterSource parameterMap ) {
185
238
186
239
if (containsSpelExpressions ) {
187
-
188
- spelEvaluator .evaluate (objects ).forEach (parameterMap ::addValue );
189
- return spelEvaluator .getQueryString ();
240
+ ValueEvaluationContext evaluationContext = delegate .createValueContextProvider (bindableParameters )
241
+ .getEvaluationContext (objects );
242
+ for (Map .Entry <String , String > entry : parameterBindings ) {
243
+ parameterMap .addValue (
244
+ entry .getKey (), delegate .parse (entry .getValue ()).evaluate (evaluationContext ));
245
+ }
246
+ return parsedQuery .getQueryString ();
190
247
}
191
248
192
249
return this .query ;
0 commit comments