diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java index 614bee69c..6a5aadaf1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java @@ -80,10 +80,10 @@ public @interface Query { String countName() default ""; /** - * Define the {@link QueryRewriter} bean that should be applied to this query after the query is full assembled. + * Define a {@link QueryRewriter} that should be applied to the query string after the query is fully assembled. * * @return * @since 3.0 */ - Class queryRewriter() default QueryRewriter.NoopQueryRewriter.class; + Class queryRewriter() default QueryRewriter.IdentityQueryRewriter.class; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java index d80a998c2..426a08df6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,33 +19,41 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; /** - * Callback to rewrite a query right before it's handed to the EntityManager. + * Callback to rewrite a query and apply sorting and pagination settings that cannot be applied based on a regularly + * detectable scheme. + *

+ * The underlying the query is the one right before it is used for query object creation, so everything that Spring Data + * and tools intends to do has been done. You can customize the query to apply final changes. Rewriting can only make + * use of already existing contextual data. That is, adding or replacing query text or reuse of bound parameters. Query + * rewriting must not add additional bindable parameters as these cannot be materialized. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.0 + * @see jakarta.persistence.EntityManager#createQuery + * @see jakarta.persistence.EntityManager#createNativeQuery */ @FunctionalInterface public interface QueryRewriter { /** - * The assembled query and current {@link Sort} settings are offered. This is the query right before it's handed to - * the EntityManager, so everything that Spring Data and tools intends to do has been done. The user is able to make - * any last minute changes.
- *
+ * Rewrite the assembled query with the given {@link Sort}. + *

* WARNING: No checks are performed before the transformed query is passed to the EntityManager. - * - * @param query - the assembled generated query, right before it's handed over to the EntityManager. - * @param sort - current {@link Sort} settings provided by the method, or {@link Sort#unsorted()}} if there are none. - * @return alter the query however you like. + * + * @param query the assembled query. + * @param sort current {@link Sort} settings provided by the method, or {@link Sort#unsorted()}} if there are none. + * @return the query to be used with the {@code EntityManager}. */ String rewrite(String query, Sort sort); /** - * This alternative is used to handle {@link Pageable}-based methods. - * - * @param query - the assembled generated query, right before it's handed over to the EntityManager. - * @param pageRequest - * @return + * Rewrite the assembled query with the given {@link Pageable}. + * + * @param query the assembled query. + * @param pageRequest current {@link Pageable} settings provided by the method, or {@link Pageable#unpaged()} if not + * paged. + * @return the query to be used with the {@code EntityManager}. */ default String rewrite(String query, Pageable pageRequest) { return rewrite(query, pageRequest.getSort()); @@ -54,7 +62,9 @@ public interface QueryRewriter { /** * A {@link QueryRewriter} that doesn't change the query. */ - public class NoopQueryRewriter implements QueryRewriter { + enum IdentityQueryRewriter implements QueryRewriter { + + INSTANCE; @Override public String rewrite(String query, Sort sort) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java similarity index 50% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java index 8a4369da8..d83a578b0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,37 +19,52 @@ import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanManager; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import java.util.Iterator; + +import org.springframework.beans.BeanUtils; import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.data.jpa.repository.query.DelegatingQueryRewriter; +import org.springframework.data.jpa.repository.query.JpaQueryMethod; import org.springframework.data.jpa.repository.query.QueryRewriterProvider; +import org.springframework.data.util.Lazy; /** * A {@link BeanManager}-based {@link QueryRewriterProvider}. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.0 */ -public class QueryRewriterBeanManagerProvider extends QueryRewriterProvider { - - private static final Log LOGGER = LogFactory.getLog(QueryRewriterBeanManagerProvider.class); +public class BeanManagerQueryRewriterProvider implements QueryRewriterProvider { private final BeanManager beanManager; - public QueryRewriterBeanManagerProvider(BeanManager beanManager) { + public BeanManagerQueryRewriterProvider(BeanManager beanManager) { this.beanManager = beanManager; } @Override - protected QueryRewriter extractQueryRewriterBean(Class queryRewriter) { + @SuppressWarnings("unchecked") + public QueryRewriter getQueryRewriter(JpaQueryMethod method) { - try { - Bean bean = (Bean) beanManager.getBeans(queryRewriter).iterator().next(); - CreationalContext context = beanManager.createCreationalContext(bean); - return (QueryRewriter) beanManager.getReference(bean, queryRewriter, context); - } catch (Exception e) { - LOGGER.error(e.toString()); - return null; + Class queryRewriter = method.getQueryRewriter(); + if (queryRewriter == QueryRewriter.IdentityQueryRewriter.class) { + return QueryRewriter.IdentityQueryRewriter.INSTANCE; } + + Iterator> iterator = beanManager.getBeans(queryRewriter).iterator(); + + if (iterator.hasNext()) { + + Bean bean = (Bean) iterator.next(); + CreationalContext context = beanManager.createCreationalContext(bean); + Lazy rewriter = Lazy + .of(() -> (QueryRewriter) beanManager.getReference(bean, queryRewriter, context)); + + return new DelegatingQueryRewriter(rewriter); + } + + return BeanUtils.instantiateClass(queryRewriter); } + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index 3f9b522d2..01412b744 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -62,7 +62,7 @@ class JpaRepositoryBean extends CdiRepositoryBean { Assert.notNull(entityManagerBean, "EntityManager bean must not be null!"); this.entityManagerBean = entityManagerBean; - this.queryRewriterProvider = new QueryRewriterBeanManagerProvider(beanManager); + this.queryRewriterProvider = new BeanManagerQueryRewriterProvider(beanManager); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 6f4eb5bdb..20611985b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -18,10 +18,9 @@ package org.springframework.data.jpa.repository.query; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; -import java.util.function.Supplier; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.QueryRewriter; @@ -46,14 +45,12 @@ import org.springframework.util.Assert; */ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { - private static final Log LOGGER = LogFactory.getLog(AbstractStringBasedJpaQuery.class); - private final DeclaredQuery query; private final DeclaredQuery countQuery; private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final SpelExpressionParser parser; private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache(); - private final Supplier queryRewriterSupplier; + private final QueryRewriter queryRewriter; /** * Creates a new {@link AbstractStringBasedJpaQuery} from the given {@link JpaQueryMethod}, {@link EntityManager} and @@ -65,16 +62,18 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { * @param countQueryString must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}. * @param parser must not be {@literal null}. + * @param queryRewriter must not be {@literal null}. */ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, - @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, - SpelExpressionParser parser, QueryRewriterProvider queryRewriterProvider) { + @Nullable String countQueryString, QueryRewriter queryRewriter, QueryMethodEvaluationContextProvider evaluationContextProvider, + SpelExpressionParser parser) { super(method, em); Assert.hasText(queryString, "Query string must not be null or empty!"); Assert.notNull(evaluationContextProvider, "ExpressionEvaluationContextProvider must not be null!"); Assert.notNull(parser, "Parser must not be null!"); + Assert.notNull(queryRewriter, "QueryRewriter must not be null!"); this.evaluationContextProvider = evaluationContextProvider; this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser, @@ -85,7 +84,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { method.isNativeQuery()); this.parser = parser; - this.queryRewriterSupplier = queryRewriterProvider.of(method); + this.queryRewriter = queryRewriter; Assert.isTrue(method.isNativeQuery() || !query.usesJdbcStyleParameters(), "JDBC style parameters (?) are not supported for JPA queries."); @@ -169,7 +168,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { /** * Use the {@link QueryRewriter}, potentially rewrite the query, using relevant {@link Sort} and {@link Pageable} * information. - * + * * @param originalQuery * @param sort * @param pageable @@ -177,12 +176,6 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { */ protected String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nullable Pageable pageable) { - QueryRewriter queryRewriter = this.queryRewriterSupplier.get(); - - if (queryRewriter == null) { - return originalQuery; - } - return pageable != null && pageable.isPaged() // ? queryRewriter.rewrite(originalQuery, pageable) // : queryRewriter.rewrite(originalQuery, sort); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java similarity index 53% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java index 102dc6c94..bf34780a4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,36 +15,38 @@ */ package org.springframework.data.jpa.repository.query; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.data.util.Lazy; /** * A {@link BeanFactory}-based {@link QueryRewriterProvider}. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.0 */ -public class QueryRewriterBeanFactoryProvider extends QueryRewriterProvider { - - private static final Log LOGGER = LogFactory.getLog(QueryRewriterBeanFactoryProvider.class); +public class BeanFactoryQueryRewriterProvider implements QueryRewriterProvider { private final BeanFactory beanFactory; - public QueryRewriterBeanFactoryProvider(BeanFactory beanFactory) { + public BeanFactoryQueryRewriterProvider(BeanFactory beanFactory) { this.beanFactory = beanFactory; } @Override - protected QueryRewriter extractQueryRewriterBean(Class queryRewriter) { + @SuppressWarnings("unchecked") + public QueryRewriter getQueryRewriter(JpaQueryMethod method) { - try { - return beanFactory.getBean(queryRewriter); - } catch (BeansException e) { - LOGGER.error(e.toString()); - return null; + Class queryRewriter = method.getQueryRewriter(); + if (queryRewriter == QueryRewriter.IdentityQueryRewriter.class) { + return QueryRewriter.IdentityQueryRewriter.INSTANCE; } + + Lazy rewriter = Lazy.of(() -> beanFactory.getBeanProvider((Class) queryRewriter) + .getIfAvailable(() -> BeanUtils.instantiateClass(queryRewriter))); + + return new DelegatingQueryRewriter(rewriter); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java new file mode 100644 index 000000000..27aec0f9a --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java @@ -0,0 +1,48 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.function.Supplier; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.QueryRewriter; + +/** + * Delegating {@link QueryRewriter} that delegates rewrite calls to a {@link QueryRewriter delegate} provided by a + * {@link Supplier}. + * + * @author Mark Paluch + * @since 3.0 + */ +public class DelegatingQueryRewriter implements QueryRewriter { + + private final Supplier delegate; + + public DelegatingQueryRewriter(Supplier delegate) { + this.delegate = delegate; + } + + @Override + public String rewrite(String query, Sort sort) { + return delegate.get().rewrite(query, sort); + } + + @Override + public String rewrite(String query, Pageable pageRequest) { + return delegate.get().rewrite(query, pageRequest); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 83df010e5..63b0bffe6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository.query; import jakarta.persistence.EntityManager; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -39,20 +40,20 @@ enum JpaQueryFactory { * * @param method must not be {@literal null}. * @param em must not be {@literal null}. - * @param queryString must not be {@literal null} or empty. * @param countQueryString + * @param queryString must not be {@literal null}. * @param evaluationContextProvider * @return */ AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, String queryString, - @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, - QueryRewriterProvider queryRewriterProvider) { + @Nullable String countQueryString, QueryRewriter queryRewriter, + QueryMethodEvaluationContextProvider evaluationContextProvider) { return method.isNativeQuery() - ? new NativeJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER, - queryRewriterProvider) - : new SimpleJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER, - queryRewriterProvider); + ? new NativeJpaQuery(method, em, queryString, countQueryString, queryRewriter, evaluationContextProvider, + PARSER) + : new SimpleJpaQuery(method, em, queryString, countQueryString, queryRewriter, evaluationContextProvider, + PARSER); } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 552c587af..5fba2b6a4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -22,6 +22,7 @@ import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryMetadata; @@ -83,14 +84,13 @@ public final class JpaQueryLookupStrategy { @Override public final RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { - return resolveQuery(queryMethodFactory.build(method, metadata, factory), em, namedQueries); + JpaQueryMethod queryMethod = queryMethodFactory.build(method, metadata, factory); + return resolveQuery(queryMethod, queryRewriterProvider.getQueryRewriter(queryMethod), em, namedQueries); } - protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries); + protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, + EntityManager em, NamedQueries namedQueries); - protected QueryRewriterProvider getQueryRewriterSupplier() { - return queryRewriterProvider; - } } /** @@ -112,7 +112,8 @@ public final class JpaQueryLookupStrategy { } @Override - protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { + protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em, + NamedQueries namedQueries) { return new PartTreeJpaQuery(method, em, escape); } } @@ -145,7 +146,8 @@ public final class JpaQueryLookupStrategy { } @Override - protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { + protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em, + NamedQueries namedQueries) { if (method.isProcedureQuery()) { return JpaQueryFactory.INSTANCE.fromProcedureAnnotation(method, em); @@ -159,13 +161,13 @@ public final class JpaQueryLookupStrategy { } return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getRequiredAnnotatedQuery(), - getCountQuery(method, namedQueries, em), evaluationContextProvider, getQueryRewriterSupplier()); + getCountQuery(method, namedQueries, em), queryRewriter, evaluationContextProvider); } String name = method.getNamedQueryName(); if (namedQueries.hasQuery(name)) { return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), - getCountQuery(method, namedQueries, em), evaluationContextProvider, getQueryRewriterSupplier()); + getCountQuery(method, namedQueries, em), queryRewriter, evaluationContextProvider); } RepositoryQuery query = NamedQuery.lookupFrom(method, em); @@ -240,12 +242,13 @@ public final class JpaQueryLookupStrategy { } @Override - protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { + protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em, + NamedQueries namedQueries) { try { - return lookupStrategy.resolveQuery(method, em, namedQueries); + return lookupStrategy.resolveQuery(method, queryRewriter, em, namedQueries); } catch (IllegalStateException e) { - return createStrategy.resolveQuery(method, em, namedQueries); + return createStrategy.resolveQuery(method, queryRewriter, em, namedQueries); } } } @@ -270,7 +273,8 @@ public final class JpaQueryLookupStrategy { case CREATE: return new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape); case USE_DECLARED_QUERY: - return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, queryRewriterProvider); + return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, + queryRewriterProvider); case CREATE_IF_NOT_FOUND: return new CreateIfNotFoundQueryLookupStrategy(em, queryMethodFactory, new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape), diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index 88e085527..e3138c5bd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -266,7 +266,7 @@ public class JpaQueryMethod extends QueryMethod { * @return */ @Nullable - String getAnnotatedQuery() { + public String getAnnotatedQuery() { String query = getAnnotationValue("value", String.class); return StringUtils.hasText(query) ? query : null; @@ -287,7 +287,7 @@ public class JpaQueryMethod extends QueryMethod { * @throws IllegalStateException if no {@link Query} annotation is present or the query is empty. * @since 2.0 */ - String getRequiredAnnotatedQuery() throws IllegalStateException { + public String getRequiredAnnotatedQuery() throws IllegalStateException { String query = getAnnotatedQuery(); @@ -305,7 +305,7 @@ public class JpaQueryMethod extends QueryMethod { * @return */ @Nullable - String getCountQuery() { + public String getCountQuery() { String countQuery = getAnnotationValue("countQuery", String.class); return StringUtils.hasText(countQuery) ? countQuery : null; @@ -438,8 +438,7 @@ public class JpaQueryMethod extends QueryMethod { * @return type of the {@link QueryRewriter} * @since 3.0 */ - @Nullable - Class getQueryRewriter() { + public Class getQueryRewriter() { return getMergedOrDefaultAnnotationValue("queryRewriter", Query.class, Class.class); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index c517ff0ae..205e7d1f2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -21,6 +21,7 @@ import jakarta.persistence.Tuple; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; @@ -48,13 +49,13 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { * @param em must not be {@literal null}. * @param queryString must not be {@literal null} or empty. * @param countQueryString must not be {@literal null} or empty. - * @param evaluationContextProvider + * @param rewriter the query rewriter to use. */ public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, - QueryRewriterProvider queryRewriterProvider) { + QueryRewriter rewriter, QueryMethodEvaluationContextProvider evaluationContextProvider, + SpelExpressionParser parser) { - super(method, em, queryString, countQueryString, evaluationContextProvider, parser, queryRewriterProvider); + super(method, em, queryString, countQueryString, rewriter, evaluationContextProvider, parser); Parameters parameters = method.getParameters(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java deleted file mode 100644 index 094e377cd..000000000 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2008-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.query; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.data.jpa.repository.QueryRewriter; - -/** - * {@link QueryRewriterProvider} that does nothing. - * - * @author Greg Turnquist - * @since 3.0 - */ -public class QueryRewriterNoopProvider extends QueryRewriterProvider { - - private static final Log LOGGER = LogFactory.getLog(QueryRewriterNoopProvider.class); - - /** - * Returns {@literal null}, signaling there is no rewriting. - * - * @param queryRewriter class definition to find in the context - * @return {@literal null} since this doesn't actually rewrite anything. - */ - @Override - public QueryRewriter extractQueryRewriterBean(Class queryRewriter) { - - LOGGER.warn("You have NOT configured JpaRepositoryFactory with a QueryRewriterProvider!"); - - return null; - } -} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java index ba8071e5c..ed955d9d5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,57 +15,45 @@ */ package org.springframework.data.jpa.repository.query; -import java.util.function.Supplier; - +import org.springframework.beans.BeanUtils; import org.springframework.data.jpa.repository.QueryRewriter; -import org.springframework.lang.Nullable; /** - * Provide a {@link QueryRewriter} based upon the {@link JpaQueryMethod} and the surrounding context (Spring, CDI, etc.) + * Provide a {@link QueryRewriter} based upon the {@link JpaQueryMethod}. {@code QueryRewriter} instances may be + * contextual or plain objects that are not attached to a bean factory or CDI context. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.0 + * @see QueryRewriter */ -public abstract class QueryRewriterProvider { +public interface QueryRewriterProvider { /** - * Using a {@link JpaQueryMethod}, extract a potential {@link QueryRewriter}. Wrap all this in a {@link Supplier} to - * defer the lookup until needed. - * - * @param method - JpaQueryMethod - * @return a {@link Supplier}-wrapped callback to fetch the {@link QueryRewriter} + * Return a simple {@code QueryRewriterProvider} that uses + * {@link org.springframework.beans.BeanUtils#instantiateClass(Class)} to obtain a {@link QueryRewriter} instance. + * + * @return a simple {@link QueryRewriterProvider}. */ - public Supplier of(JpaQueryMethod method) { - return () -> findQueryRewriter(method); + static QueryRewriterProvider simple() { + + return method -> { + + Class queryRewriter = method.getQueryRewriter(); + + if (queryRewriter == QueryRewriter.IdentityQueryRewriter.class) { + return QueryRewriter.IdentityQueryRewriter.INSTANCE; + } + + return BeanUtils.instantiateClass(queryRewriter); + }; } /** - * Using the {@link org.springframework.data.jpa.repository.QueryRewrite} annotation, look for a {@link QueryRewriter} - * and instantiate one. NOTE: If its {@link QueryRewriter.NoopQueryRewriter}, it will just return {@literal null} and - * NOT do any rewrite operations. + * Obtain an instance of {@link QueryRewriter} for a {@link JpaQueryMethod}. * - * @param method - {@link JpaQueryMethod} that has the annotation details - * @return a {@link QueryRewriter for the method or {@code null} + * @param method the underlying JPA query method. + * @return a Java bean that implements {@link QueryRewriter}. */ - @Nullable - private QueryRewriter findQueryRewriter(JpaQueryMethod method) { - - Class queryRewriter = method.getQueryRewriter(); - - if (queryRewriter == null || queryRewriter == QueryRewriter.NoopQueryRewriter.class) { - return null; - } - - return extractQueryRewriterBean(queryRewriter); - } - - /** - * Extract an instance of {@link QueryRewriter} from the context. Implementations choose what context means, whether - * that is Spring, CDI, or whatever. - * - * @param queryRewriter - * @return a Java bean that implements {@link QueryRewriter}. {@literal null} is valid if no bean is found. - */ - @Nullable - protected abstract QueryRewriter extractQueryRewriterBean(Class queryRewriter); + QueryRewriter getQueryRewriter(JpaQueryMethod method); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index 6e48f10bd..1e765db9c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.query; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -41,14 +42,13 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { * @param method must not be {@literal null} * @param em must not be {@literal null} * @param countQueryString + * @param queryRewriter must not be {@literal null} * @param evaluationContextProvider must not be {@literal null} * @param parser must not be {@literal null} */ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, - QueryRewriterProvider queryRewriterProvider) { - this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, evaluationContextProvider, parser, - queryRewriterProvider); + QueryRewriter queryRewriter, QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { + this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, queryRewriter, evaluationContextProvider, parser); } /** @@ -58,14 +58,14 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { * @param em must not be {@literal null} * @param queryString must not be {@literal null} or empty * @param countQueryString + * @param queryRewriter * @param evaluationContextProvider must not be {@literal null} * @param parser must not be {@literal null} */ - public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, - QueryRewriterProvider queryRewriterProvider) { + public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, QueryRewriter queryRewriter, + QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { - super(method, em, queryString, countQueryString, evaluationContextProvider, parser, queryRewriterProvider); + super(method, em, queryString, countQueryString, queryRewriter, evaluationContextProvider, parser); validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s!", method); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 367d0bf92..46f8624c7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -27,6 +27,8 @@ import java.util.stream.Stream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.dao.InvalidDataAccessApiUsageException; @@ -34,7 +36,15 @@ import org.springframework.data.jpa.projection.CollectionAwareProjectionFactory; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.query.*; +import org.springframework.data.jpa.repository.query.AbstractJpaQuery; +import org.springframework.data.jpa.repository.query.BeanFactoryQueryRewriterProvider; +import org.springframework.data.jpa.repository.query.DefaultJpaQueryMethodFactory; +import org.springframework.data.jpa.repository.query.EscapeCharacter; +import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy; +import org.springframework.data.jpa.repository.query.JpaQueryMethod; +import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory; +import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.jpa.repository.query.QueryRewriterProvider; import org.springframework.data.jpa.util.JpaMetamodel; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.querydsl.EntityPathResolver; @@ -93,12 +103,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor(); this.entityPathResolver = SimpleEntityPathResolver.INSTANCE; this.queryMethodFactory = new DefaultJpaQueryMethodFactory(extractor); - - /** - * Default to {@link QueryRewriterNoopProvider}. If there is a {@link BeanFactory} or {@link BeanManager}, this will - * result in later overriding this with the proper version. - */ - this.queryRewriterProvider = new QueryRewriterNoopProvider(); + this.queryRewriterProvider = QueryRewriterProvider.simple(); addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor); addRepositoryProxyPostProcessor((factory, repositoryInformation) -> { @@ -122,8 +127,8 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { /** * If a {@link BeanFactory} is being set, this is clearly in a Spring context, and so we can capture the - * {@link QueryRewriterProvider} being a {@link QueryRewriterBeanFactoryProvider}. - * + * {@link QueryRewriterProvider} being a {@link BeanFactoryQueryRewriterProvider}. + * * @param beanFactory * @throws BeansException */ @@ -134,7 +139,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { Assert.notNull(beanFactory, "BeanFactory must not be null!"); - setQueryRewriterProvider(new QueryRewriterBeanFactoryProvider(beanFactory)); + setQueryRewriterProvider(new BeanFactoryQueryRewriterProvider(beanFactory)); } /** @@ -171,9 +176,11 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { } /** - * Configures the {@link QueryRewriterProvider} to be used. Defaults to {@link QueryRewriterNoopProvider}. + * Configures the {@link QueryRewriterProvider} to be used. Defaults to instantiate query rewriters through + * {@link BeanUtils#instantiateClass(Class)}. * - * @param queryRewriterProvider must not be {@literal null} + * @param queryRewriterProvider must not be {@literal null}. + * @since 3.0 */ public void setQueryRewriterProvider(QueryRewriterProvider queryRewriterProvider) { @@ -254,7 +261,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { * * @param metadata repository metadata. * @param entityManager the entity manager. - * @param resolver resolver to translate an plain domain class into a {@link EntityPath}. + * @param resolver resolver to translate a plain domain class into a {@link EntityPath}. * @param crudMethodMetadata metadata about the invoked CRUD methods. * @return * @since 2.5.1 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java index aae03b374..3077542d7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ import org.springframework.data.repository.cdi.Eager; /** * Unit tests for repository with {@link Query} and {@link QueryRewrite} in a CDI environment. - * + * * @author Greg Turnquist */ public class JpaQueryRewriterWithCdiIntegrationTests { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index 09f7789b9..33abbeebb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -34,6 +34,7 @@ import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; @@ -66,9 +67,8 @@ public class AbstractStringBasedJpaQueryIntegrationTests { when(mock.getMetamodel()).thenReturn(em.getMetamodel()); JpaQueryMethod method = getMethod("findRolesByEmailAddress", String.class); - AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, null, - QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser(), - new QueryRewriterBeanFactoryProvider(beanFactory)); + AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, null, QueryRewriter.IdentityQueryRewriter.INSTANCE, + QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser()); jpaQuery.createJpaQuery(method.getAnnotatedQuery(), Sort.unsorted(), null, method.getResultProcessor().getReturnedType()); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index ed71fc826..0ba5d98e9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -89,7 +89,7 @@ public class JpaQueryLookupStrategyUnitTests { void invalidAnnotatedQueryCausesException() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("findByFoo", String.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); @@ -105,7 +105,7 @@ public class JpaQueryLookupStrategyUnitTests { void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("findByInvalidNativeQuery", String.class, Sort.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); @@ -119,7 +119,7 @@ public class JpaQueryLookupStrategyUnitTests { void considersNamedCountQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); when(namedQueries.hasQuery("foo.count")).thenReturn(true); when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); @@ -141,7 +141,7 @@ public class JpaQueryLookupStrategyUnitTests { void considersNamedCountOnStringQueryQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); when(namedQueries.hasQuery("foo.count")).thenReturn(true); when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); @@ -160,7 +160,7 @@ public class JpaQueryLookupStrategyUnitTests { void prefersDeclaredQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("annotatedQueryWithQueryAndQueryName"); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java index f11d645fc..61d0db240 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; /** * Unit tests for repository with {@link Query} and {@link QueryRewrite}. - * + * * @author Greg Turnquist */ @ExtendWith(SpringExtension.class) @@ -64,7 +64,7 @@ public class JpaQueryRewriteIntegrationTests { } @Test - void nativeQueryShouldHandleRewrites() throws NoSuchMethodException { + void nativeQueryShouldHandleRewrites() { repository.findByNativeQuery("Matthews"); @@ -75,7 +75,7 @@ public class JpaQueryRewriteIntegrationTests { } @Test - void nonNativeQueryShouldHandleRewrites() throws NoSuchMethodException { + void nonNativeQueryShouldHandleRewrites() { repository.findByNonNativeQuery("Matthews"); @@ -86,7 +86,7 @@ public class JpaQueryRewriteIntegrationTests { } @Test - void nonNativeQueryWithSortShouldHandleRewrites() throws NoSuchMethodException { + void nonNativeQueryWithSortShouldHandleRewrites() { repository.findByNonNativeSortedQuery("Matthews", Sort.by("lastname")); @@ -108,7 +108,7 @@ public class JpaQueryRewriteIntegrationTests { } @Test - void nonNativeQueryWithPageableShouldHandleRewrites() throws NoSuchMethodException { + void nonNativeQueryWithPageableShouldHandleRewrites() { repository.findByNonNativePagedQuery("Matthews", PageRequest.of(2, 1)); @@ -119,7 +119,7 @@ public class JpaQueryRewriteIntegrationTests { } @Test - void nativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethodException { + void nativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() { repository.findByNativeQueryWithNoRewrite("Matthews"); @@ -127,7 +127,7 @@ public class JpaQueryRewriteIntegrationTests { } @Test - void nonNativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethodException { + void nonNativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() { repository.findByNonNativeQueryWithNoRewrite("Matthews"); @@ -135,7 +135,7 @@ public class JpaQueryRewriteIntegrationTests { } @Test - void nativeQueryShouldHandleRewritesUsingRepositoryRewriter() throws NoSuchMethodException { + void nativeQueryShouldHandleRewritesUsingRepositoryRewriter() { repository.findByNativeQueryUsingRepository("Matthews"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index d394bfc5c..8ebb5a4e8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -37,7 +37,7 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; -import org.springframework.beans.factory.BeanFactory; + import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -45,6 +45,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -82,7 +83,6 @@ class SimpleJpaQueryUnitTests { @Mock RepositoryMetadata metadata; @Mock ParameterBinder binder; @Mock Metamodel metamodel; - @Mock BeanFactory beanFactory; private ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); @@ -113,8 +113,8 @@ class SimpleJpaQueryUnitTests { extractor); when(em.createQuery("foo", Long.class)).thenReturn(typedQuery); - SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", null, EVALUATION_CONTEXT_PROVIDER, - PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); + SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", null, + QueryRewriter.IdentityQueryRewriter.INSTANCE, EVALUATION_CONTEXT_PROVIDER, PARSER); assertThat(jpaQuery.createCountQuery(new JpaParametersParameterAccessor(method.getParameters(), new Object[] {}))) .isEqualTo(typedQuery); @@ -129,7 +129,7 @@ class SimpleJpaQueryUnitTests { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", null, - EVALUATION_CONTEXT_PROVIDER, PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); + QueryRewriter.IdentityQueryRewriter.INSTANCE, EVALUATION_CONTEXT_PROVIDER, PARSER); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -144,7 +144,8 @@ class SimpleJpaQueryUnitTests { Method method = SampleRepository.class.getMethod("findNativeByLastname", String.class); JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, - queryMethod.getAnnotatedQuery(), null, EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory)); + queryMethod.getAnnotatedQuery(), null, QueryRewriter.IdentityQueryRewriter.INSTANCE, + EVALUATION_CONTEXT_PROVIDER); assertThat(jpaQuery instanceof NativeJpaQuery).isTrue(); @@ -247,7 +248,8 @@ class SimpleJpaQueryUnitTests { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", - "select count(u.id) from #{#entityName} u", EVALUATION_CONTEXT_PROVIDER, PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); + "select count(u.id) from #{#entityName} u", QueryRewriter.IdentityQueryRewriter.INSTANCE, + EVALUATION_CONTEXT_PROVIDER, PARSER); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -259,7 +261,7 @@ class SimpleJpaQueryUnitTests { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, queryMethod.getAnnotatedQuery(), null, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory)); + QueryRewriter.IdentityQueryRewriter.INSTANCE, EVALUATION_CONTEXT_PROVIDER); } interface SampleRepository {