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,65 @@ 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.
176
+ *
177
+ * @param method - {@link JpaQueryMethod} that has the annotation details
178
+ * @return a {@link QueryRewriter for the method or {@code null}
179
+ */
180
+ @ Nullable
181
+ private QueryRewriter findQueryRewriter (JpaQueryMethod method ) {
182
+
183
+ Class <? extends QueryRewriter > queryRewriter = method .getQueryRewriter ();
184
+
185
+ if (queryRewriter == null ) {
186
+ return null ;
187
+ }
188
+
189
+ try {
190
+ return beanFactory .getBean (queryRewriter );
191
+ } catch (BeansException e ) {
192
+ LOGGER .error (e .toString ());
193
+ return null ;
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Use the {@link QueryRewriter}, potentially rewrite the query, using relevant {@link Sort} and {@link Pageable}
199
+ * information.
200
+ *
201
+ * @param originalQuery
202
+ * @param sort
203
+ * @param pageable
204
+ * @return
205
+ */
206
+ protected String potentiallyRewriteQuery (String originalQuery , Sort sort , @ Nullable Pageable pageable ) {
207
+
208
+ QueryRewriter queryRewriter = this .queryRewriterSupplier .get ();
209
+
210
+ if (queryRewriter == null ) {
211
+ return originalQuery ;
212
+ }
213
+
214
+ return pageable != null && pageable .isPaged () //
215
+ ? queryRewriter .rewrite (originalQuery , pageable ) //
216
+ : queryRewriter .rewrite (originalQuery , sort );
153
217
}
154
218
}
0 commit comments