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
This commit is contained in:
@@ -112,7 +112,6 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
|
||||
});
|
||||
|
||||
this.countParameterBinder = Lazy.of(() -> this.createBinder(this.countQuery.get()));
|
||||
|
||||
this.queryRewriter = queryConfiguration.getQueryRewriter(method);
|
||||
|
||||
JpaParameters parameters = method.getParameters();
|
||||
|
||||
@@ -169,7 +169,7 @@ public final class JpaQueryLookupStrategy {
|
||||
configuration);
|
||||
}
|
||||
|
||||
RepositoryQuery query = NamedQuery.lookupFrom(method, em, configuration.getSelector());
|
||||
RepositoryQuery query = NamedQuery.lookupFrom(method, em, configuration);
|
||||
|
||||
return query != null ? query : NO_QUERY;
|
||||
}
|
||||
|
||||
@@ -56,11 +56,12 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
private final @Nullable String countProjection;
|
||||
private final boolean namedCountQueryIsPresent;
|
||||
private final Lazy<EntityQuery> entityQuery;
|
||||
private final QueryRewriter queryRewriter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link NamedQuery}.
|
||||
*/
|
||||
private NamedQuery(JpaQueryMethod method, EntityManager em, QueryEnhancerSelector selector, QueryRewriter queryRewriter) {
|
||||
private NamedQuery(JpaQueryMethod method, EntityManager em, JpaQueryConfiguration queryConfiguration) {
|
||||
|
||||
super(method, em);
|
||||
|
||||
@@ -68,7 +69,7 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
this.countQueryName = method.getNamedCountQueryName();
|
||||
QueryExtractor extractor = method.getQueryExtractor();
|
||||
this.countProjection = method.getCountQueryProjection();
|
||||
this.queryRewriter = queryRewriter;
|
||||
this.queryRewriter = queryConfiguration.getQueryRewriter(method);
|
||||
|
||||
Parameters<?, ?> parameters = method.getParameters();
|
||||
|
||||
@@ -104,7 +105,7 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
declaredQuery = DeclaredQuery.jpqlQuery(queryString);
|
||||
}
|
||||
|
||||
this.entityQuery = Lazy.of(() -> EntityQuery.create(declaredQuery, selector));
|
||||
this.entityQuery = Lazy.of(() -> EntityQuery.create(declaredQuery, queryConfiguration.getSelector()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,9 +139,10 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
* @param method must not be {@literal null}.
|
||||
* @param em must not be {@literal null}.
|
||||
* @param selector must not be {@literal null}.
|
||||
* @param queryConfiguration must not be {@literal null}.
|
||||
*/
|
||||
public static @Nullable RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em,
|
||||
QueryEnhancerSelector selector) {
|
||||
JpaQueryConfiguration queryConfiguration) {
|
||||
|
||||
String queryName = method.getNamedQueryName();
|
||||
|
||||
@@ -158,7 +160,7 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
method.isNativeQuery() ? "NativeQuery" : "Query"));
|
||||
}
|
||||
|
||||
RepositoryQuery query = new NamedQuery(method, em, selector);
|
||||
RepositoryQuery query = new NamedQuery(method, em, queryConfiguration);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Found named query '%s'", queryName));
|
||||
}
|
||||
@@ -193,6 +195,7 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
} else {
|
||||
|
||||
String countQueryString = entityQuery.get().deriveCountQuery(countProjection).getQueryString();
|
||||
countQueryString = potentiallyRewriteQuery(countQueryString, accessor.getSort(), accessor.getPageable());
|
||||
countQuery = em.createQuery(countQueryString, Long.class);
|
||||
}
|
||||
|
||||
@@ -235,9 +238,9 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
private String potentiallyRewriteQuery(String originalQuery, Sort sort, Pageable pageable) {
|
||||
private String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nullable Pageable pageable) {
|
||||
|
||||
return pageable.isPaged() //
|
||||
return pageable != null && pageable.isPaged() //
|
||||
? queryRewriter.rewrite(originalQuery, pageable) //
|
||||
: queryRewriter.rewrite(originalQuery, sort);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryCreationException;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
@@ -55,6 +56,9 @@ import org.springframework.data.util.TypeInformation;
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class NamedQueryUnitTests {
|
||||
|
||||
private static final JpaQueryConfiguration CONFIG = new JpaQueryConfiguration(QueryRewriterProvider.simple(),
|
||||
QueryEnhancerSelector.DEFAULT_SELECTOR, ValueExpressionDelegate.create(), EscapeCharacter.DEFAULT);
|
||||
|
||||
@Mock RepositoryMetadata metadata;
|
||||
@Mock QueryExtractor extractor;
|
||||
@Mock EntityManager em;
|
||||
@@ -89,7 +93,8 @@ class NamedQueryUnitTests {
|
||||
JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, projectionFactory, extractor);
|
||||
|
||||
when(em.createNamedQuery(queryMethod.getNamedCountQueryName())).thenThrow(new IllegalArgumentException());
|
||||
assertThatExceptionOfType(QueryCreationException.class).isThrownBy(() -> NamedQuery.lookupFrom(queryMethod, em, QueryEnhancerSelector.DEFAULT_SELECTOR, QueryRewriter.IdentityQueryRewriter.INSTANCE));
|
||||
assertThatExceptionOfType(QueryCreationException.class)
|
||||
.isThrownBy(() -> NamedQuery.lookupFrom(queryMethod, em, CONFIG));
|
||||
}
|
||||
|
||||
@Test // DATAJPA-142
|
||||
@@ -101,7 +106,7 @@ class NamedQueryUnitTests {
|
||||
|
||||
TypedQuery<Long> countQuery = mock(TypedQuery.class);
|
||||
when(em.createNamedQuery(eq(queryMethod.getNamedCountQueryName()), eq(Long.class))).thenReturn(countQuery);
|
||||
NamedQuery query = (NamedQuery) NamedQuery.lookupFrom(queryMethod, em, QueryEnhancerSelector.DEFAULT_SELECTOR, QueryRewriter.IdentityQueryRewriter.INSTANCE);
|
||||
NamedQuery query = (NamedQuery) NamedQuery.lookupFrom(queryMethod, em, CONFIG);
|
||||
|
||||
query.doCreateCountQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[1]));
|
||||
verify(em, times(1)).createNamedQuery(queryMethod.getNamedCountQueryName(), Long.class);
|
||||
|
||||
@@ -376,6 +376,8 @@ Sometimes, no matter how many features you try to apply, it seems impossible to
|
||||
|
||||
You have the ability to get your hands on the query, right before it's sent to the `EntityManager` and "rewrite" it.
|
||||
That is, you can make any alterations at the last moment.
|
||||
Query rewriting applies to the actual query and, when applicable, to count queries.
|
||||
Count queries are optimized and therefore, either not necessary or a count is obtained through other means, such as derived from a Hibernate `SelectionQuery`.
|
||||
|
||||
.Declare a QueryRewriter using `@Query`
|
||||
====
|
||||
|
||||
Reference in New Issue
Block a user