1
1
/*
2
- * Copyright 2018-2021 the original author or authors.
2
+ * Copyright 2018-2022 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
21
21
22
22
import org .apache .commons .logging .Log ;
23
23
import org .apache .commons .logging .LogFactory ;
24
-
25
24
import org .springframework .beans .factory .BeanFactory ;
26
25
import org .springframework .context .ApplicationEventPublisher ;
27
26
import org .springframework .data .jdbc .core .convert .EntityRowMapper ;
41
40
import org .springframework .data .relational .core .mapping .event .AfterLoadEvent ;
42
41
import org .springframework .data .repository .core .NamedQueries ;
43
42
import org .springframework .data .repository .core .RepositoryMetadata ;
44
- import org .springframework .data .repository .query .QueryCreationException ;
45
43
import org .springframework .data .repository .query .QueryLookupStrategy ;
46
44
import org .springframework .data .repository .query .RepositoryQuery ;
47
45
import org .springframework .jdbc .core .RowMapper ;
51
49
import org .springframework .util .Assert ;
52
50
53
51
/**
54
- * {@link QueryLookupStrategy} for JDBC repositories.
52
+ * Abstract {@link QueryLookupStrategy} for JDBC repositories.
55
53
*
56
54
* @author Jens Schauder
57
55
* @author Kazuki Shimizu
60
58
* @author Maciej Walkowiak
61
59
* @author Moises Cisneros
62
60
* @author Hebert Coelho
61
+ * @author Diego Krupitza
63
62
*/
64
- class JdbcQueryLookupStrategy implements QueryLookupStrategy {
63
+ abstract class JdbcQueryLookupStrategy implements QueryLookupStrategy {
65
64
66
65
private static final Log LOG = LogFactory .getLog (JdbcQueryLookupStrategy .class );
67
66
@@ -72,12 +71,12 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
72
71
private final Dialect dialect ;
73
72
private final QueryMappingConfiguration queryMappingConfiguration ;
74
73
private final NamedParameterJdbcOperations operations ;
75
- private final BeanFactory beanfactory ;
74
+ @ Nullable private final BeanFactory beanfactory ;
76
75
77
76
JdbcQueryLookupStrategy (ApplicationEventPublisher publisher , @ Nullable EntityCallbacks callbacks ,
78
77
RelationalMappingContext context , JdbcConverter converter , Dialect dialect ,
79
78
QueryMappingConfiguration queryMappingConfiguration , NamedParameterJdbcOperations operations ,
80
- BeanFactory beanfactory ) {
79
+ @ Nullable BeanFactory beanfactory ) {
81
80
82
81
Assert .notNull (publisher , "ApplicationEventPublisher must not be null" );
83
82
Assert .notNull (context , "RelationalMappingContextPublisher must not be null" );
@@ -96,43 +95,209 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
96
95
this .beanfactory = beanfactory ;
97
96
}
98
97
99
- /*
100
- * (non-Javadoc)
101
- * @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
98
+ /**
99
+ * {@link QueryLookupStrategy} to create a query from the method name.
100
+ *
101
+ * @author Diego Krupitza
102
102
*/
103
- @ Override
104
- public RepositoryQuery resolveQuery (Method method , RepositoryMetadata repositoryMetadata ,
105
- ProjectionFactory projectionFactory , NamedQueries namedQueries ) {
103
+ static class CreateQueryLookupStrategy extends JdbcQueryLookupStrategy {
104
+
105
+ CreateQueryLookupStrategy (ApplicationEventPublisher publisher , @ Nullable EntityCallbacks callbacks ,
106
+ RelationalMappingContext context , JdbcConverter converter , Dialect dialect ,
107
+ QueryMappingConfiguration queryMappingConfiguration , NamedParameterJdbcOperations operations ,
108
+ @ Nullable BeanFactory beanfactory ) {
109
+ super (publisher , callbacks , context , converter , dialect , queryMappingConfiguration , operations , beanfactory );
110
+ }
111
+
112
+ @ Override
113
+ public RepositoryQuery resolveQuery (Method method , RepositoryMetadata repositoryMetadata ,
114
+ ProjectionFactory projectionFactory , NamedQueries namedQueries ) {
115
+
116
+ JdbcQueryMethod queryMethod = getJdbcQueryMethod (method , repositoryMetadata , projectionFactory , namedQueries );
117
+
118
+ return new PartTreeJdbcQuery (getContext (), queryMethod , getDialect (), getConverter (), getOperations (),
119
+ this ::createMapper );
120
+ }
121
+ }
122
+
123
+ /**
124
+ * {@link QueryLookupStrategy} that tries to detect a declared query declared via
125
+ * {@link org.springframework.data.jdbc.repository.query.Query} annotation followed by a JPA named query lookup.
126
+ *
127
+ * @author Diego Krupitza
128
+ */
129
+ static class DeclaredQueryLookupStrategy extends JdbcQueryLookupStrategy {
130
+
131
+ DeclaredQueryLookupStrategy (ApplicationEventPublisher publisher , @ Nullable EntityCallbacks callbacks ,
132
+ RelationalMappingContext context , JdbcConverter converter , Dialect dialect ,
133
+ QueryMappingConfiguration queryMappingConfiguration , NamedParameterJdbcOperations operations ,
134
+ @ Nullable BeanFactory beanfactory ) {
135
+ super (publisher , callbacks , context , converter , dialect , queryMappingConfiguration , operations , beanfactory );
136
+ }
137
+
138
+ @ Override
139
+ public RepositoryQuery resolveQuery (Method method , RepositoryMetadata repositoryMetadata ,
140
+ ProjectionFactory projectionFactory , NamedQueries namedQueries ) {
106
141
107
- JdbcQueryMethod queryMethod = new JdbcQueryMethod (method , repositoryMetadata , projectionFactory , namedQueries ,
108
- context );
142
+ JdbcQueryMethod queryMethod = getJdbcQueryMethod (method , repositoryMetadata , projectionFactory , namedQueries );
109
143
110
- try {
111
144
if (namedQueries .hasQuery (queryMethod .getNamedQueryName ()) || queryMethod .hasAnnotatedQuery ()) {
112
145
113
146
if (queryMethod .hasAnnotatedQuery () && queryMethod .hasAnnotatedQueryName ()) {
114
147
LOG .warn (String .format (
115
148
"Query method %s is annotated with both, a query and a query name. Using the declared query." , method ));
116
149
}
117
150
118
- StringBasedJdbcQuery query = new StringBasedJdbcQuery (queryMethod , operations , this ::createMapper , converter );
119
- query .setBeanFactory (beanfactory );
151
+ StringBasedJdbcQuery query = new StringBasedJdbcQuery (queryMethod , getOperations (), this ::createMapper ,
152
+ getConverter ());
153
+ query .setBeanFactory (getBeanfactory ());
120
154
return query ;
121
- } else {
122
- return new PartTreeJdbcQuery (context , queryMethod , dialect , converter , operations , this ::createMapper );
123
155
}
124
- } catch (Exception e ) {
125
- throw QueryCreationException .create (queryMethod , e );
156
+
157
+ throw new IllegalStateException (
158
+ String .format ("Did neither find a NamedQuery nor an annotated query for method %s!" , method ));
159
+ }
160
+ }
161
+
162
+ /**
163
+ * {@link QueryLookupStrategy} to try to detect a declared query first (
164
+ * {@link org.springframework.data.jdbc.repository.query.Query}, JDBC named query). In case none is found we fall back
165
+ * on query creation.
166
+ * <p>
167
+ * Modified based on original source: {@link org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy}
168
+ *
169
+ * @author Oliver Gierke
170
+ * @author Thomas Darimont
171
+ * @author Diego Krupitza
172
+ */
173
+ static class CreateIfNotFoundQueryLookupStrategy extends JdbcQueryLookupStrategy {
174
+
175
+ private final DeclaredQueryLookupStrategy lookupStrategy ;
176
+ private final CreateQueryLookupStrategy createStrategy ;
177
+
178
+ /**
179
+ * Creates a new {@link CreateIfNotFoundQueryLookupStrategy}.
180
+ *
181
+ * @param createStrategy must not be {@literal null}.
182
+ * @param lookupStrategy must not be {@literal null}.
183
+ */
184
+ public CreateIfNotFoundQueryLookupStrategy (ApplicationEventPublisher publisher , @ Nullable EntityCallbacks callbacks ,
185
+ RelationalMappingContext context , JdbcConverter converter , Dialect dialect ,
186
+ QueryMappingConfiguration queryMappingConfiguration , NamedParameterJdbcOperations operations ,
187
+ @ Nullable BeanFactory beanfactory , CreateQueryLookupStrategy createStrategy ,
188
+ DeclaredQueryLookupStrategy lookupStrategy ) {
189
+
190
+ super (publisher , callbacks , context , converter , dialect , queryMappingConfiguration , operations , beanfactory );
191
+
192
+ Assert .notNull (createStrategy , "CreateQueryLookupStrategy must not be null!" );
193
+ Assert .notNull (lookupStrategy , "DeclaredQueryLookupStrategy must not be null!" );
194
+
195
+ this .createStrategy = createStrategy ;
196
+ this .lookupStrategy = lookupStrategy ;
126
197
}
198
+
199
+ @ Override
200
+ public RepositoryQuery resolveQuery (Method method , RepositoryMetadata repositoryMetadata ,
201
+ ProjectionFactory projectionFactory , NamedQueries namedQueries ) {
202
+
203
+ try {
204
+ return lookupStrategy .resolveQuery (method , repositoryMetadata , projectionFactory , namedQueries );
205
+ } catch (IllegalStateException e ) {
206
+ return createStrategy .resolveQuery (method , repositoryMetadata , projectionFactory , namedQueries );
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Creates a {@link JdbcQueryMethod} based on the parameters
213
+ */
214
+ JdbcQueryMethod getJdbcQueryMethod (Method method , RepositoryMetadata repositoryMetadata ,
215
+ ProjectionFactory projectionFactory , NamedQueries namedQueries ) {
216
+ return new JdbcQueryMethod (method , repositoryMetadata , projectionFactory , namedQueries , context );
217
+ }
218
+
219
+ /**
220
+ * Creates a {@link QueryLookupStrategy} based on the provided
221
+ * {@link org.springframework.data.repository.query.QueryLookupStrategy.Key}.
222
+ *
223
+ * @param key the key that decides what {@link QueryLookupStrategy} shozld be used.
224
+ * @param publisher must not be {@literal null}
225
+ * @param callbacks may be {@literal null}
226
+ * @param context must not be {@literal null}
227
+ * @param converter must not be {@literal null}
228
+ * @param dialect must not be {@literal null}
229
+ * @param queryMappingConfiguration must not be {@literal null}
230
+ * @param operations must not be {@literal null}
231
+ * @param beanfactory may be {@literal null}
232
+ */
233
+ public static QueryLookupStrategy create (@ Nullable Key key , ApplicationEventPublisher publisher ,
234
+ @ Nullable EntityCallbacks callbacks , RelationalMappingContext context , JdbcConverter converter , Dialect dialect ,
235
+ QueryMappingConfiguration queryMappingConfiguration , NamedParameterJdbcOperations operations ,
236
+ @ Nullable BeanFactory beanfactory ) {
237
+
238
+ Assert .notNull (publisher , "ApplicationEventPublisher must not be null" );
239
+ Assert .notNull (context , "RelationalMappingContextPublisher must not be null" );
240
+ Assert .notNull (converter , "JdbcConverter must not be null" );
241
+ Assert .notNull (dialect , "Dialect must not be null" );
242
+ Assert .notNull (queryMappingConfiguration , "QueryMappingConfiguration must not be null" );
243
+ Assert .notNull (operations , "NamedParameterJdbcOperations must not be null" );
244
+
245
+ CreateQueryLookupStrategy createQueryLookupStrategy = new CreateQueryLookupStrategy (publisher , callbacks , context ,
246
+ converter , dialect , queryMappingConfiguration , operations , beanfactory );
247
+
248
+ DeclaredQueryLookupStrategy declaredQueryLookupStrategy = new DeclaredQueryLookupStrategy (publisher , callbacks ,
249
+ context , converter , dialect , queryMappingConfiguration , operations , beanfactory );
250
+
251
+ Key cleanedKey = key != null ? key : Key .CREATE_IF_NOT_FOUND ;
252
+
253
+ LOG .debug (String .format ("Using the queryLookupStrategy %s" , cleanedKey ));
254
+
255
+ switch (cleanedKey ) {
256
+ case CREATE :
257
+ return createQueryLookupStrategy ;
258
+ case USE_DECLARED_QUERY :
259
+ return declaredQueryLookupStrategy ;
260
+ case CREATE_IF_NOT_FOUND :
261
+ return new CreateIfNotFoundQueryLookupStrategy (publisher , callbacks , context , converter , dialect ,
262
+ queryMappingConfiguration , operations , beanfactory , createQueryLookupStrategy , declaredQueryLookupStrategy );
263
+ default :
264
+ throw new IllegalArgumentException (String .format ("Unsupported query lookup strategy %s!" , key ));
265
+ }
266
+ }
267
+
268
+ protected ApplicationEventPublisher getPublisher () {
269
+ return publisher ;
270
+ }
271
+
272
+ protected RelationalMappingContext getContext () {
273
+ return context ;
274
+ }
275
+
276
+ protected JdbcConverter getConverter () {
277
+ return converter ;
278
+ }
279
+
280
+ protected Dialect getDialect () {
281
+ return dialect ;
282
+ }
283
+
284
+ protected NamedParameterJdbcOperations getOperations () {
285
+ return operations ;
286
+ }
287
+
288
+ @ Nullable
289
+ protected BeanFactory getBeanfactory () {
290
+ return beanfactory ;
127
291
}
128
292
129
293
@ SuppressWarnings ("unchecked" )
130
- private RowMapper <Object > createMapper (Class <?> returnedObjectType ) {
294
+ RowMapper <Object > createMapper (Class <?> returnedObjectType ) {
131
295
132
296
RelationalPersistentEntity <?> persistentEntity = context .getPersistentEntity (returnedObjectType );
133
297
134
298
if (persistentEntity == null ) {
135
- return (RowMapper <Object >) SingleColumnRowMapper .newInstance (returnedObjectType , converter .getConversionService ());
299
+ return (RowMapper <Object >) SingleColumnRowMapper .newInstance (returnedObjectType ,
300
+ converter .getConversionService ());
136
301
}
137
302
138
303
return (RowMapper <Object >) determineDefaultMapper (returnedObjectType );
0 commit comments