diff --git a/pom.xml b/pom.xml index dd2134e5d1..d86598d842 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-gh-2989-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index f239d6394b..70ab172953 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-SNAPSHOT + 3.2.0-gh-2989-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-gh-2989-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a458953182..36d9fadf38 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-gh-2989-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index b21b03c313..e0964095ca 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-SNAPSHOT + 3.2.0-gh-2989-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-gh-2989-SNAPSHOT ../pom.xml 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 d626507f24..d762b41bd7 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 @@ -22,6 +22,7 @@ import java.lang.annotation.Target; import org.springframework.data.annotation.QueryAnnotation; +import org.springframework.data.jpa.repository.query.QueryEnhancerOption; /** * Annotation to declare finder queries directly on repository methods. @@ -86,4 +87,12 @@ * @since 3.0 */ Class queryRewriter() default QueryRewriter.IdentityQueryRewriter.class; + + /** + * For native queries, indicate whether or not to skip the JSqlParser. + * + * @return + * @since 3.2 + */ + QueryEnhancerOption queryEnhancerOption() default QueryEnhancerOption.AUTOMATIC_BEST_FIT; } 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 cf22cc9f40..44a5ba2d2e 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 @@ -75,7 +75,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri this.evaluationContextProvider = evaluationContextProvider; this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser, - method.isNativeQuery()); + method.isNativeQuery(), method.queryEnhancerOption()); this.countQuery = Lazy.of(() -> { DeclaredQuery countQuery = query.deriveCountQuery(countQueryString, method.getCountQueryProjection()); @@ -92,7 +92,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri @Override public Query doCreateQuery(JpaParametersParameterAccessor accessor) { - String sortedQueryString = QueryEnhancerFactory.forQuery(query) // + String sortedQueryString = query.queryEnhancerOption().forQuery(query) // .applySorting(accessor.getSort(), query.getAlias()); ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java index 670e030fb2..32c83c944a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java @@ -111,4 +111,8 @@ default boolean usesPaging() { default boolean isNativeQuery() { return false; } + + default QueryEnhancerOption queryEnhancerOption() { + return QueryEnhancerOption.AUTOMATIC_BEST_FIT; + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index 3bee9c2d48..bcad881564 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -59,9 +59,15 @@ class ExpressionBasedStringQuery extends StringQuery { * @param parser must not be {@literal null}. * @param nativeQuery is a given query is native or not */ + public ExpressionBasedStringQuery(String query, JpaEntityMetadata metadata, SpelExpressionParser parser, + boolean nativeQuery, QueryEnhancerOption queryEnhancerOption) { + super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser), nativeQuery && !containsExpression(query), + queryEnhancerOption); + } + public ExpressionBasedStringQuery(String query, JpaEntityMetadata metadata, SpelExpressionParser parser, boolean nativeQuery) { - super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser), nativeQuery && !containsExpression(query)); + this(query, metadata, parser, nativeQuery, QueryEnhancerOption.AUTOMATIC_BEST_FIT); } /** @@ -75,7 +81,7 @@ public ExpressionBasedStringQuery(String query, JpaEntityMetadata metadata, S */ static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata metadata, SpelExpressionParser parser, boolean nativeQuery) { - return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser, nativeQuery); + return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser, nativeQuery, query.queryEnhancerOption()); } /** 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 ed566ba52c..9894f369ab 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 @@ -94,6 +94,7 @@ public class JpaQueryMethod extends QueryMethod { private final Lazy jpaEntityGraph; private final Lazy modifying; private final Lazy isNativeQuery; + private final Lazy queryEnhancerOption; private final Lazy isCollectionQuery; private final Lazy isProcedureQuery; private final Lazy> entityMetadata; @@ -136,6 +137,7 @@ protected JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionF return new JpaEntityGraph(entityGraph, getNamedQueryName()); }); this.isNativeQuery = Lazy.of(() -> getAnnotationValue("nativeQuery", Boolean.class)); + this.queryEnhancerOption = Lazy.of(() -> getAnnotationValue("queryEnhancerOption", QueryEnhancerOption.class)); this.isCollectionQuery = Lazy.of(() -> super.isCollectionQuery() && !NATIVE_ARRAY_TYPES.contains(this.returnType)); this.isProcedureQuery = Lazy.of(() -> AnnotationUtils.findAnnotation(method, Procedure.class) != null); this.entityMetadata = Lazy.of(() -> new DefaultJpaEntityMetadata<>(getDomainClass())); @@ -387,6 +389,10 @@ boolean isNativeQuery() { return this.isNativeQuery.get(); } + QueryEnhancerOption queryEnhancerOption() { + return this.queryEnhancerOption.get(); + } + @Override public String getNamedQueryName() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java index 74aa77e611..62a26b1d8e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java @@ -15,10 +15,6 @@ */ package org.springframework.data.jpa.repository.query; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.util.ClassUtils; - /** * Encapsulates different strategies for the creation of a {@link QueryEnhancer} from a {@link DeclaredQuery}. * @@ -29,48 +25,35 @@ */ public final class QueryEnhancerFactory { - private static final Log LOG = LogFactory.getLog(QueryEnhancerFactory.class); - - private static final boolean jSqlParserPresent = ClassUtils.isPresent("net.sf.jsqlparser.parser.JSqlParser", - QueryEnhancerFactory.class.getClassLoader()); - - private static final boolean hibernatePresent = ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", - QueryEnhancerFactory.class.getClassLoader()); - - static { - - if (jSqlParserPresent) { - LOG.info("JSqlParser is in classpath; If applicable, JSqlParser will be used"); - } - - if (hibernatePresent) { - LOG.info("Hibernate is in classpath; If applicable, HQL parser will be used."); - } - } - - private QueryEnhancerFactory() {} - - /** - * Creates a new {@link QueryEnhancer} for the given {@link DeclaredQuery}. - * - * @param query must not be {@literal null}. - * @return an implementation of {@link QueryEnhancer} that suits the query the most - */ - public static QueryEnhancer forQuery(DeclaredQuery query) { - - if (query.isNativeQuery()) { - - if (jSqlParserPresent) { - /* - * If JSqlParser fails, throw some alert signaling that people should write a custom Impl. - */ - return new JSqlParserQueryEnhancer(query); - } - - return new DefaultQueryEnhancer(query); - } - - return hibernatePresent ? JpaQueryEnhancer.forHql(query) : JpaQueryEnhancer.forJpql(query); - } + // private static final Log LOG = LogFactory.getLog(QueryEnhancerFactory.class); + // + // private static final boolean jSqlParserPresent = ClassUtils.isPresent("net.sf.jsqlparser.parser.JSqlParser", + // QueryEnhancerFactory.class.getClassLoader()); + // + // private static final boolean hibernatePresent = ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", + // QueryEnhancerFactory.class.getClassLoader()); + // + // static { + // + // if (jSqlParserPresent) { + // LOG.info("JSqlParser is in classpath; If applicable, JSqlParser will be used"); + // } + // + // if (hibernatePresent) { + // LOG.info("Hibernate is in classpath; If applicable, HQL parser will be used."); + // } + // } + // + // private QueryEnhancerFactory() {} + // + // /** + // * Creates a new {@link QueryEnhancer} for the given {@link DeclaredQuery}. + // * + // * @param query must not be {@literal null}. + // * @return an implementation of {@link QueryEnhancer} that suits the query the most + // */ + // public static QueryEnhancer forQuery(DeclaredQuery query) { + // return QueryEnhancerOption.DEFAULT.forQuery(query); + // } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerOption.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerOption.java new file mode 100644 index 0000000000..19df27376d --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerOption.java @@ -0,0 +1,113 @@ +/* + * Copyright 2022-2023 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.util.ClassUtils; + +/** + * Encapsulates different strategies for the creation of a {@link QueryEnhancer} from a {@link DeclaredQuery}. + * + * @author Diego Krupitza + * @author Greg Turnquist + * @author Mark Paluch + * @since 3.2.0 + */ +public enum QueryEnhancerOption implements QueryEnhancerStrategy { + + /** + * Automatically pick the best fit based on availability of JSqlParser and Hibernate. + */ + AUTOMATIC_BEST_FIT { + @Override + public QueryEnhancer forQuery(DeclaredQuery query) { + + if (query.isNativeQuery()) { + + if (jSqlParserPresent) { + /* + * If JSqlParser fails, throw some alert signaling that people should write a custom Impl. + */ + return new JSqlParserQueryEnhancer(query); + } + + return new DefaultQueryEnhancer(query); + } + + return hibernatePresent ? JpaQueryEnhancer.forHql(query) : JpaQueryEnhancer.forJpql(query); + } + }, + + /** + * Automatically pick the best fit based on availability of Hibernate. NOTE: JSqlParser is not + * considered, even if it's on the classpath. + */ + AUTOMATIC_BEST_FIT_WITHOUT_JSQL { + @Override + public QueryEnhancer forQuery(DeclaredQuery query) { + + if (query.isNativeQuery()) { + + LOG.warn("We are NOT evaluating if JSqlParser is on the classpath in order to handle '" + query.getQueryString() + + "'"); + + return new DefaultQueryEnhancer(query); + } + + return hibernatePresent ? JpaQueryEnhancer.forHql(query) : JpaQueryEnhancer.forJpql(query); + } + }, + + MANUAL_HIBERNATE { + @Override + public QueryEnhancer forQuery(DeclaredQuery query) { + + LOG.warn("You have chosen HIBERNATE to handle '" + query.getQueryString() + "'"); + + return JpaQueryEnhancer.forHql(query); + } + }, + + MANUAL_JPA { + @Override + public QueryEnhancer forQuery(DeclaredQuery query) { + + LOG.warn("You have chose JPA to handle '" + query.getQueryString() + "'"); + + return JpaQueryEnhancer.forJpql(query); + } + }; + + private static final Log LOG = LogFactory.getLog(QueryEnhancerFactory.class); + + private static final boolean jSqlParserPresent = ClassUtils.isPresent("net.sf.jsqlparser.parser.JSqlParser", + QueryEnhancerOption.class.getClassLoader()); + + private static final boolean hibernatePresent = ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", + QueryEnhancerOption.class.getClassLoader()); + + static { + + if (jSqlParserPresent) { + LOG.info("JSqlParser is in classpath; If applicable, JSqlParser will be used"); + } + + if (hibernatePresent) { + LOG.info("Hibernate is in classpath; If applicable, HQL parser will be used."); + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerStrategy.java new file mode 100644 index 0000000000..4d723aa40c --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerStrategy.java @@ -0,0 +1,28 @@ +/* + * Copyright 2022-2023 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; + +/** + * Defines the contract for extracting a {@link QueryEnhancer} from a {@link DeclaredQuery}. + * + * @author Greg Turnquist + * @since 3.2 + */ +public interface QueryEnhancerStrategy { + + QueryEnhancer forQuery(DeclaredQuery query); + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index afa68ff51c..baa40b8907 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -65,6 +65,7 @@ class StringQuery implements DeclaredQuery { private final boolean containsPageableInSpel; private final boolean usesJdbcStyleParameters; private final boolean isNative; + private final QueryEnhancerOption queryEnhancerOption; private final QueryEnhancer queryEnhancer; /** @@ -73,11 +74,12 @@ class StringQuery implements DeclaredQuery { * @param query must not be {@literal null} or empty. */ @SuppressWarnings("deprecation") - StringQuery(String query, boolean isNative) { + StringQuery(String query, boolean isNative, QueryEnhancerOption queryEnhancerOption) { Assert.hasText(query, "Query must not be null or empty"); this.isNative = isNative; + this.queryEnhancerOption = queryEnhancerOption; this.bindings = new ArrayList<>(); this.containsPageableInSpel = query.contains("#pageable"); @@ -87,11 +89,15 @@ class StringQuery implements DeclaredQuery { this.usesJdbcStyleParameters = queryMeta.usesJdbcStyleParameters; - this.queryEnhancer = QueryEnhancerFactory.forQuery(this); + this.queryEnhancer = queryEnhancerOption.forQuery(this); this.alias = this.queryEnhancer.detectAlias(); this.hasConstructorExpression = this.queryEnhancer.hasConstructorExpression(); } + StringQuery(String query, boolean isNative) { + this(query, isNative, QueryEnhancerOption.AUTOMATIC_BEST_FIT); + } + /** * Returns whether we have found some like bindings. */ @@ -157,6 +163,11 @@ public boolean isNativeQuery() { return isNative; } + @Override + public QueryEnhancerOption queryEnhancerOption() { + return queryEnhancerOption; + } + /** * A parser that extracts the parameter bindings from a given query string. * @@ -279,7 +290,6 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St parameterIndex = expressionParameterIndex; } - BindingIdentifier queryParameter; if (parameterIndex != null) { queryParameter = BindingIdentifier.of(parameterIndex); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 9264185849..5800f65f8e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -2914,6 +2914,19 @@ void supportsProjectionsWithNativeQueries() { assertThat(result.getLastname()).isEqualTo(user.getLastname()); } + @Test // GH-2989 + void supportsProjectionsWithNativeQueriesSkippingJSql() { + + flushTestUsers(); + + User user = repository.findAll().get(0); + + NameOnly result = repository.findByNativeQueryWithNoJSql(user.getId()); + + assertThat(result.getFirstname()).isEqualTo(user.getFirstname()); + assertThat(result.getLastname()).isEqualTo(user.getLastname()); + } + @Test // DATAJPA-1248 void supportsProjectionsWithNativeQueriesAndCamelCaseProperty() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java index 2f84a4c1e1..008531bc43 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java @@ -61,7 +61,7 @@ void setOperationListWorks() { + "select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE"; StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); assertThat(stringQuery.getAlias()).isNullOrEmpty(); assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); @@ -84,7 +84,7 @@ void complexSetOperationListWorks() { + "union select SOME_COLUMN from SOME_OTHER_OTHER_TABLE"; StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); assertThat(stringQuery.getAlias()).isNullOrEmpty(); assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); @@ -111,7 +111,7 @@ void deeplyNestedcomplexSetOperationListWorks() { + "\t;"; StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); assertThat(stringQuery.getAlias()).isNullOrEmpty(); assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("CustomerID"); @@ -131,7 +131,7 @@ void valuesStatementsWorks() { String setQuery = "VALUES (1, 2, 'test')"; StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); assertThat(stringQuery.getAlias()).isNullOrEmpty(); assertThat(stringQuery.getProjection()).isNullOrEmpty(); @@ -152,7 +152,7 @@ void withStatementsWorks() { + "select day, value from sample_data as a"; StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); assertThat(stringQuery.getAlias()).isEqualToIgnoringCase("a"); assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("day, value"); @@ -175,7 +175,7 @@ void multipleWithStatementsWorks() { + "select day, value from sample_data as a"; StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); assertThat(stringQuery.getAlias()).isEqualToIgnoringCase("a"); assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("day, value"); @@ -195,7 +195,7 @@ void multipleWithStatementsWorks() { void truncateStatementShouldWork() { StringQuery stringQuery = new StringQuery("TRUNCATE TABLE foo", true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); assertThat(stringQuery.getAlias()).isNull(); assertThat(stringQuery.getProjection()).isEmpty(); @@ -213,7 +213,7 @@ void truncateStatementShouldWork() { void mergeStatementWorksWithJSqlParser(String query, String alias) { StringQuery stringQuery = new StringQuery(query, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); assertThat(queryEnhancer.detectAlias()).isEqualTo(alias); assertThat(QueryUtils.detectAlias(query)).isNull(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java index ef90549fd4..b2b4869a23 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java @@ -32,7 +32,7 @@ void createsParsingImplementationForNonNativeQuery() { StringQuery query = new StringQuery("select new com.example.User(u.firstname) from User u", false); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query); + QueryEnhancer queryEnhancer = query.queryEnhancerOption().forQuery(query); assertThat(queryEnhancer) // .isInstanceOf(JpaQueryEnhancer.class); @@ -47,7 +47,7 @@ void createsJSqlImplementationForNativeQuery() { StringQuery query = new StringQuery("select * from User", true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query); + QueryEnhancer queryEnhancer = query.queryEnhancerOption().forQuery(query); assertThat(queryEnhancer) // .isInstanceOf(JSqlParserQueryEnhancer.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java index 6140b313bf..598791eca3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java @@ -622,7 +622,17 @@ void modifyingQueriesAreDetectedCorrectly() { assertThat(modiQuery.hasConstructorExpression()).isEqualTo(constructorExpressionNotConsideringQueryType); assertThat(countQueryForNotConsiderQueryType).isEqualToIgnoringCase(modifyingQuery); - assertThat(QueryEnhancerFactory.forQuery(modiQuery).createCountQueryFor()).isEqualToIgnoringCase(modifyingQuery); + assertThat(modiQuery.queryEnhancerOption().forQuery(modiQuery).createCountQueryFor()) + .isEqualToIgnoringCase(modifyingQuery); + } + + @Test // GH-2989 + void skippingJSqlShouldRevertToDefaultQueryEnhancer() { + + assertThat(getEnhancer(new StringQuery(QUERY, true, QueryEnhancerOption.AUTOMATIC_BEST_FIT))) + .isInstanceOf(JSqlParserQueryEnhancer.class); + assertThat(getEnhancer(new StringQuery(QUERY, true, QueryEnhancerOption.AUTOMATIC_BEST_FIT_WITHOUT_JSQL))) + .isInstanceOf(DefaultQueryEnhancer.class); } @ParameterizedTest // GH-2593 @@ -630,7 +640,7 @@ void modifyingQueriesAreDetectedCorrectly() { void insertStatementIsProcessedSameAsDefault(String insertQuery) { StringQuery stringQuery = new StringQuery(insertQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + QueryEnhancer queryEnhancer = stringQuery.queryEnhancerOption().forQuery(stringQuery); Sort sorting = Sort.by("day").descending(); @@ -686,7 +696,7 @@ private static void assertCountQuery(StringQuery originalQuery, String countQuer } private static QueryEnhancer getEnhancer(DeclaredQuery query) { - return QueryEnhancerFactory.forQuery(query); + return query.queryEnhancerOption().forQuery(query); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index c9a342538f..2288801d88 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -44,6 +44,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.QueryHints; import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.jpa.repository.query.QueryEnhancerOption; import org.springframework.data.querydsl.ListQuerydslPredicateExecutor; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; @@ -585,6 +586,11 @@ List findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true) NameOnly findByNativeQuery(Integer id); + // GH-2989 + @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true, + queryEnhancerOption = QueryEnhancerOption.AUTOMATIC_BEST_FIT_WITHOUT_JSQL) + NameOnly findByNativeQueryWithNoJSql(Integer id); + // DATAJPA-1248 @Query(value = "SELECT emailaddress FROM SD_User WHERE id = ?1", nativeQuery = true) EmailOnly findEmailOnlyByNativeQuery(Integer id);