18
18
import jakarta .persistence .EntityManager ;
19
19
import jakarta .persistence .Query ;
20
20
21
+ import java .util .function .Supplier ;
22
+
23
+ import org .apache .commons .logging .Log ;
24
+ import org .apache .commons .logging .LogFactory ;
25
+ import org .springframework .beans .BeansException ;
26
+ import org .springframework .beans .factory .BeanFactory ;
27
+ import org .springframework .data .domain .Pageable ;
28
+ import org .springframework .data .domain .Sort ;
29
+ import org .springframework .data .jpa .repository .QueryRewriter ;
21
30
import org .springframework .data .repository .query .QueryMethodEvaluationContextProvider ;
22
31
import org .springframework .data .repository .query .ResultProcessor ;
23
32
import org .springframework .data .repository .query .ReturnedType ;
35
44
* @author David Madden
36
45
* @author Mark Paluch
37
46
* @author Diego Krupitza
47
+ * @author Greg Turnquist
38
48
*/
39
49
abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
40
50
51
+ private static final Log LOGGER = LogFactory .getLog (AbstractStringBasedJpaQuery .class );
52
+
41
53
private final DeclaredQuery query ;
42
54
private final DeclaredQuery countQuery ;
43
55
private final QueryMethodEvaluationContextProvider evaluationContextProvider ;
44
56
private final SpelExpressionParser parser ;
45
57
private final QueryParameterSetter .QueryMetadataCache metadataCache = new QueryParameterSetter .QueryMetadataCache ();
58
+ private final Supplier <QueryRewriter > queryRewriterSupplier ;
59
+ private final BeanFactory beanFactory ;
46
60
47
61
/**
48
62
* Creates a new {@link AbstractStringBasedJpaQuery} from the given {@link JpaQueryMethod}, {@link EntityManager} and
@@ -57,7 +71,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
57
71
*/
58
72
public AbstractStringBasedJpaQuery (JpaQueryMethod method , EntityManager em , String queryString ,
59
73
@ Nullable String countQueryString , QueryMethodEvaluationContextProvider evaluationContextProvider ,
60
- SpelExpressionParser parser ) {
74
+ SpelExpressionParser parser , BeanFactory beanFactory ) {
61
75
62
76
super (method , em );
63
77
@@ -74,6 +88,8 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri
74
88
method .isNativeQuery ());
75
89
76
90
this .parser = parser ;
91
+ this .beanFactory = beanFactory ;
92
+ this .queryRewriterSupplier = () -> findQueryRewriter (method );
77
93
78
94
Assert .isTrue (method .isNativeQuery () || !query .usesJdbcStyleParameters (),
79
95
"JDBC style parameters (?) are not supported for JPA queries." );
@@ -86,7 +102,8 @@ public Query doCreateQuery(JpaParametersParameterAccessor accessor) {
86
102
.applySorting (accessor .getSort (), query .getAlias ());
87
103
ResultProcessor processor = getQueryMethod ().getResultProcessor ().withDynamicProjection (accessor );
88
104
89
- Query query = createJpaQuery (sortedQueryString , processor .getReturnedType ());
105
+ Query query = createJpaQuery (sortedQueryString , accessor .getSort (), accessor .getPageable (),
106
+ processor .getReturnedType ());
90
107
91
108
QueryParameterSetter .QueryMetadata metadata = metadataCache .getMetadata (sortedQueryString , query );
92
109
@@ -137,18 +154,66 @@ public DeclaredQuery getCountQuery() {
137
154
* Creates an appropriate JPA query from an {@link EntityManager} according to the current {@link AbstractJpaQuery}
138
155
* type.
139
156
*/
140
- protected Query createJpaQuery (String queryString , ReturnedType returnedType ) {
157
+ protected Query createJpaQuery (String queryString , Sort sort , @ Nullable Pageable pageable ,
158
+ ReturnedType returnedType ) {
141
159
142
160
EntityManager em = getEntityManager ();
143
161
144
162
if (this .query .hasConstructorExpression () || this .query .isDefaultProjection ()) {
145
- return em .createQuery (queryString );
163
+ return em .createQuery (potentiallyRewriteQuery ( queryString , sort , pageable ) );
146
164
}
147
165
148
166
Class <?> typeToRead = getTypeToRead (returnedType );
149
167
150
168
return typeToRead == null //
151
- ? em .createQuery (queryString ) //
152
- : em .createQuery (queryString , typeToRead );
169
+ ? em .createQuery (potentiallyRewriteQuery (queryString , sort , pageable )) //
170
+ : em .createQuery (potentiallyRewriteQuery (queryString , sort , pageable ), typeToRead );
171
+ }
172
+
173
+ /**
174
+ * Using the {@link org.springframework.data.jpa.repository.QueryRewrite} annotation, look for a {@link QueryRewriter}
175
+ * and instantiate one. NOTE: If its {@link QueryRewriter.NoopQueryRewriter}, it
176
+ * will just return {@literal null} and NOT do any rewrite operations.
177
+ *
178
+ * @param method - {@link JpaQueryMethod} that has the annotation details
179
+ * @return a {@link QueryRewriter for the method or {@code null}
180
+ */
181
+ @ Nullable
182
+ private QueryRewriter findQueryRewriter (JpaQueryMethod method ) {
183
+
184
+ Class <? extends QueryRewriter > queryRewriter = method .getQueryRewriter ();
185
+
186
+ if (queryRewriter == null || queryRewriter == QueryRewriter .NoopQueryRewriter .class ) {
187
+ return null ;
188
+ }
189
+
190
+ try {
191
+ return beanFactory .getBean (queryRewriter );
192
+ } catch (BeansException e ) {
193
+ LOGGER .error (e .toString ());
194
+ return null ;
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Use the {@link QueryRewriter}, potentially rewrite the query, using relevant {@link Sort} and {@link Pageable}
200
+ * information.
201
+ *
202
+ * @param originalQuery
203
+ * @param sort
204
+ * @param pageable
205
+ * @return
206
+ */
207
+ protected String potentiallyRewriteQuery (String originalQuery , Sort sort , @ Nullable Pageable pageable ) {
208
+
209
+ QueryRewriter queryRewriter = this .queryRewriterSupplier .get ();
210
+
211
+ if (queryRewriter == null ) {
212
+ return originalQuery ;
213
+ }
214
+
215
+ return pageable != null && pageable .isPaged () //
216
+ ? queryRewriter .rewrite (originalQuery , pageable ) //
217
+ : queryRewriter .rewrite (originalQuery , sort );
153
218
}
154
219
}
0 commit comments