Skip to content

Commit 9a6575b

Browse files
committed
Apply QueryRewriter to count queries as well.
We now use QueryRewriter to post-process count queries as well. Previously, only the actual result query has been processed. Closes #3801
1 parent 5efeb56 commit 9a6575b

File tree

5 files changed

+20
-11
lines changed

5 files changed

+20
-11
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Decl
112112
});
113113

114114
this.countParameterBinder = Lazy.of(() -> this.createBinder(this.countQuery.get()));
115-
116115
this.queryRewriter = queryConfiguration.getQueryRewriter(method);
117116

118117
JpaParameters parameters = method.getParameters();

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, JpaQueryConfigurat
169169
configuration);
170170
}
171171

172-
RepositoryQuery query = NamedQuery.lookupFrom(method, em, configuration.getSelector());
172+
RepositoryQuery query = NamedQuery.lookupFrom(method, em, configuration);
173173

174174
return query != null ? query : NO_QUERY;
175175
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,20 @@ final class NamedQuery extends AbstractJpaQuery {
5656
private final @Nullable String countProjection;
5757
private final boolean namedCountQueryIsPresent;
5858
private final Lazy<EntityQuery> entityQuery;
59+
private final QueryRewriter queryRewriter;
5960

6061
/**
6162
* Creates a new {@link NamedQuery}.
6263
*/
63-
private NamedQuery(JpaQueryMethod method, EntityManager em, QueryEnhancerSelector selector, QueryRewriter queryRewriter) {
64+
private NamedQuery(JpaQueryMethod method, EntityManager em, JpaQueryConfiguration queryConfiguration) {
6465

6566
super(method, em);
6667

6768
this.queryName = method.getNamedQueryName();
6869
this.countQueryName = method.getNamedCountQueryName();
6970
QueryExtractor extractor = method.getQueryExtractor();
7071
this.countProjection = method.getCountQueryProjection();
71-
this.queryRewriter = queryRewriter;
72+
this.queryRewriter = queryConfiguration.getQueryRewriter(method);
7273

7374
Parameters<?, ?> parameters = method.getParameters();
7475

@@ -104,7 +105,7 @@ private NamedQuery(JpaQueryMethod method, EntityManager em, QueryEnhancerSelecto
104105
declaredQuery = DeclaredQuery.jpqlQuery(queryString);
105106
}
106107

107-
this.entityQuery = Lazy.of(() -> EntityQuery.create(declaredQuery, selector));
108+
this.entityQuery = Lazy.of(() -> EntityQuery.create(declaredQuery, queryConfiguration.getSelector()));
108109
}
109110

110111
/**
@@ -138,9 +139,10 @@ static boolean hasNamedQuery(EntityManager em, String queryName) {
138139
* @param method must not be {@literal null}.
139140
* @param em must not be {@literal null}.
140141
* @param selector must not be {@literal null}.
142+
* @param queryConfiguration must not be {@literal null}.
141143
*/
142144
public static @Nullable RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em,
143-
QueryEnhancerSelector selector) {
145+
JpaQueryConfiguration queryConfiguration) {
144146

145147
String queryName = method.getNamedQueryName();
146148

@@ -158,7 +160,7 @@ static boolean hasNamedQuery(EntityManager em, String queryName) {
158160
method.isNativeQuery() ? "NativeQuery" : "Query"));
159161
}
160162

161-
RepositoryQuery query = new NamedQuery(method, em, selector);
163+
RepositoryQuery query = new NamedQuery(method, em, queryConfiguration);
162164
if (LOG.isDebugEnabled()) {
163165
LOG.debug(String.format("Found named query '%s'", queryName));
164166
}
@@ -193,6 +195,7 @@ protected TypedQuery<Long> doCreateCountQuery(JpaParametersParameterAccessor acc
193195
} else {
194196

195197
String countQueryString = entityQuery.get().deriveCountQuery(countProjection).getQueryString();
198+
countQueryString = potentiallyRewriteQuery(countQueryString, accessor.getSort(), accessor.getPageable());
196199
countQuery = em.createQuery(countQueryString, Long.class);
197200
}
198201

@@ -235,9 +238,9 @@ protected TypedQuery<Long> doCreateCountQuery(JpaParametersParameterAccessor acc
235238
* @param pageable
236239
* @return
237240
*/
238-
private String potentiallyRewriteQuery(String originalQuery, Sort sort, Pageable pageable) {
241+
private String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nullable Pageable pageable) {
239242

240-
return pageable.isPaged() //
243+
return pageable != null && pageable.isPaged() //
241244
? queryRewriter.rewrite(originalQuery, pageable) //
242245
: queryRewriter.rewrite(originalQuery, sort);
243246
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
4242
import org.springframework.data.repository.core.RepositoryMetadata;
4343
import org.springframework.data.repository.query.QueryCreationException;
44+
import org.springframework.data.repository.query.ValueExpressionDelegate;
4445
import org.springframework.data.util.TypeInformation;
4546

4647
/**
@@ -55,6 +56,9 @@
5556
@MockitoSettings(strictness = Strictness.LENIENT)
5657
class NamedQueryUnitTests {
5758

59+
private static final JpaQueryConfiguration CONFIG = new JpaQueryConfiguration(QueryRewriterProvider.simple(),
60+
QueryEnhancerSelector.DEFAULT_SELECTOR, ValueExpressionDelegate.create(), EscapeCharacter.DEFAULT);
61+
5862
@Mock RepositoryMetadata metadata;
5963
@Mock QueryExtractor extractor;
6064
@Mock EntityManager em;
@@ -89,7 +93,8 @@ void rejectsPersistenceProviderIfIncapableOfExtractingQueriesAndPagebleBeingUsed
8993
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, projectionFactory, extractor);
9094

9195
when(em.createNamedQuery(queryMethod.getNamedCountQueryName())).thenThrow(new IllegalArgumentException());
92-
assertThatExceptionOfType(QueryCreationException.class).isThrownBy(() -> NamedQuery.lookupFrom(queryMethod, em, QueryEnhancerSelector.DEFAULT_SELECTOR, QueryRewriter.IdentityQueryRewriter.INSTANCE));
96+
assertThatExceptionOfType(QueryCreationException.class)
97+
.isThrownBy(() -> NamedQuery.lookupFrom(queryMethod, em, CONFIG));
9398
}
9499

95100
@Test // DATAJPA-142
@@ -101,7 +106,7 @@ void doesNotRejectPersistenceProviderIfNamedCountQueryIsAvailable() {
101106

102107
TypedQuery<Long> countQuery = mock(TypedQuery.class);
103108
when(em.createNamedQuery(eq(queryMethod.getNamedCountQueryName()), eq(Long.class))).thenReturn(countQuery);
104-
NamedQuery query = (NamedQuery) NamedQuery.lookupFrom(queryMethod, em, QueryEnhancerSelector.DEFAULT_SELECTOR, QueryRewriter.IdentityQueryRewriter.INSTANCE);
109+
NamedQuery query = (NamedQuery) NamedQuery.lookupFrom(queryMethod, em, CONFIG);
105110

106111
query.doCreateCountQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[1]));
107112
verify(em, times(1)).createNamedQuery(queryMethod.getNamedCountQueryName(), Long.class);

src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,8 @@ Sometimes, no matter how many features you try to apply, it seems impossible to
376376

377377
You have the ability to get your hands on the query, right before it's sent to the `EntityManager` and "rewrite" it.
378378
That is, you can make any alterations at the last moment.
379+
Query rewriting applies to the actual query and, when applicable, to count queries.
380+
Count queries are optimized and therefore, either not necessary or a count is obtained through other means, such as derived from a Hibernate `SelectionQuery`.
379381

380382
.Declare a QueryRewriter using `@Query`
381383
====

0 commit comments

Comments
 (0)