diff --git a/pom.xml b/pom.xml index 2311b378e6..570b3b32b8 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.4.0-SNAPSHOT + 3.4.x-1690-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 8b836ae2f3..e97239edb5 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.4.0-SNAPSHOT + 3.4.x-1690-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.4.0-SNAPSHOT + 3.4.x-1690-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index e9e1754bc3..c5881f7430 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.4.0-SNAPSHOT + 3.4.x-1690-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-performance/pom.xml b/spring-data-jpa-performance/pom.xml index fa836f34c6..ef2e9e4bb1 100644 --- a/spring-data-jpa-performance/pom.xml +++ b/spring-data-jpa-performance/pom.xml @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.4.0-SNAPSHOT + 3.4.x-1690-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 0f9c5d717e..6d8d852adb 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.4.0-SNAPSHOT + 3.4.x-1690-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.4.0-SNAPSHOT + 3.4.x-1690-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 7010542f45..948df4fa1f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -83,6 +83,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions"; private static final String JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME = "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup"; private static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter"; + private static final String QUERY_VALIDATION = "queryValidation"; private final Map entityManagerRefs = new LinkedHashMap<>(); @@ -118,6 +119,7 @@ public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSo builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME)); builder.addPropertyReference("entityManager", entityManagerRefs.get(source)); builder.addPropertyValue(ESCAPE_CHARACTER_PROPERTY, getEscapeCharacter(source).orElse('\\')); + getQueryValidation(source).ifPresent(it -> builder.addPropertyValue(QUERY_VALIDATION, it)); builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME); } @@ -139,6 +141,15 @@ private static Optional getEscapeCharacter(RepositoryConfigurationSou } } + private static Optional getQueryValidation(RepositoryConfigurationSource source) { + + try { + return source.getAttribute(QUERY_VALIDATION, Boolean.class); + } catch (IllegalArgumentException ___) { + return Optional.empty(); + } + } + @Override public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) { 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 a998e1a086..38e8949158 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 @@ -38,7 +38,7 @@ enum JpaQueryFactory { */ AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, QueryRewriter queryRewriter, - ValueExpressionDelegate valueExpressionDelegate) { + ValueExpressionDelegate valueExpressionDelegate, boolean validation) { if (method.isScrollQuery()) { throw QueryCreationException.create(method, "Scroll queries are not supported using String-based queries"); @@ -46,7 +46,7 @@ AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager return method.isNativeQuery() ? new NativeJpaQuery(method, em, queryString, countQueryString, queryRewriter, valueExpressionDelegate) - : new SimpleJpaQuery(method, em, queryString, countQueryString, queryRewriter, valueExpressionDelegate); + : new SimpleJpaQuery(method, em, queryString, countQueryString, queryRewriter, valueExpressionDelegate, validation); } /** 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 136a16b271..e996d26310 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 @@ -113,19 +113,21 @@ protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewr private static class CreateQueryLookupStrategy extends AbstractQueryLookupStrategy { private final EscapeCharacter escape; + private final boolean validate; public CreateQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory, - QueryRewriterProvider queryRewriterProvider, EscapeCharacter escape) { + QueryRewriterProvider queryRewriterProvider, EscapeCharacter escape, boolean validate) { super(em, queryMethodFactory, queryRewriterProvider); this.escape = escape; + this.validate = validate; } @Override protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em, NamedQueries namedQueries) { - return new PartTreeJpaQuery(method, em, escape); + return new PartTreeJpaQuery(method, em, escape, validate); } } @@ -140,20 +142,24 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter quer private static class DeclaredQueryLookupStrategy extends AbstractQueryLookupStrategy { private final ValueExpressionDelegate valueExpressionDelegate; + private final boolean validation; /** * Creates a new {@link DeclaredQueryLookupStrategy}. * * @param em must not be {@literal null}. * @param queryMethodFactory must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. + * @param delegate must not be {@literal null}. + * @param queryRewriterProvider must not be {@literal null}. + * @param validation indicate if queries are validated on startup. */ public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory, - ValueExpressionDelegate delegate, QueryRewriterProvider queryRewriterProvider) { + ValueExpressionDelegate delegate, QueryRewriterProvider queryRewriterProvider, boolean validation) { super(em, queryMethodFactory, queryRewriterProvider); this.valueExpressionDelegate = delegate; + this.validation = validation; } @Override @@ -172,13 +178,13 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter quer } return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getRequiredAnnotatedQuery(), - getCountQuery(method, namedQueries, em), queryRewriter, valueExpressionDelegate); + getCountQuery(method, namedQueries, em), queryRewriter, valueExpressionDelegate, validation); } String name = method.getNamedQueryName(); if (namedQueries.hasQuery(name)) { return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), - getCountQuery(method, namedQueries, em), queryRewriter, valueExpressionDelegate); + getCountQuery(method, namedQueries, em), queryRewriter, valueExpressionDelegate, validation); } RepositoryQuery query = NamedQuery.lookupFrom(method, em); @@ -285,6 +291,22 @@ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory queryRewriterProvider, escape); } + /** + * Creates a {@link QueryLookupStrategy} for the given {@link EntityManager} and {@link Key}. + * + * @param em must not be {@literal null}. + * @param queryMethodFactory must not be {@literal null}. + * @param key may be {@literal null}. + * @param delegate must not be {@literal null}. + * @param queryRewriterProvider must not be {@literal null}. + * @param escape must not be {@literal null}. + */ + public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory queryMethodFactory, + @Nullable Key key, ValueExpressionDelegate delegate, QueryRewriterProvider queryRewriterProvider, + EscapeCharacter escape) { + return create(em, queryMethodFactory, key, delegate, queryRewriterProvider, escape, true); + } + /** * Creates a {@link QueryLookupStrategy} for the given {@link EntityManager} and {@link Key}. * @@ -297,18 +319,18 @@ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory */ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory queryMethodFactory, @Nullable Key key, ValueExpressionDelegate delegate, QueryRewriterProvider queryRewriterProvider, - EscapeCharacter escape) { + EscapeCharacter escape, boolean queryValidation) { Assert.notNull(em, "EntityManager must not be null"); Assert.notNull(delegate, "ValueExpressionDelegate must not be null"); return switch (key != null ? key : Key.CREATE_IF_NOT_FOUND) { - case CREATE -> new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape); + case CREATE -> new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape, queryValidation); case USE_DECLARED_QUERY -> - new DeclaredQueryLookupStrategy(em, queryMethodFactory, delegate, queryRewriterProvider); + new DeclaredQueryLookupStrategy(em, queryMethodFactory, delegate, queryRewriterProvider, queryValidation); case CREATE_IF_NOT_FOUND -> new CreateIfNotFoundQueryLookupStrategy(em, queryMethodFactory, - new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape), - new DeclaredQueryLookupStrategy(em, queryMethodFactory, delegate, queryRewriterProvider), + new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape, queryValidation), + new DeclaredQueryLookupStrategy(em, queryMethodFactory, delegate, queryRewriterProvider, queryValidation), queryRewriterProvider); default -> throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s", key)); }; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 1d0923f26d..895d3184a5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -71,7 +71,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { * @param em must not be {@literal null}. */ PartTreeJpaQuery(JpaQueryMethod method, EntityManager em) { - this(method, em, EscapeCharacter.DEFAULT); + this(method, em, EscapeCharacter.DEFAULT, true); } /** @@ -81,7 +81,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { * @param em must not be {@literal null}. * @param escape character used for escaping characters used as patterns in LIKE-expressions. */ - PartTreeJpaQuery(JpaQueryMethod method, EntityManager em, EscapeCharacter escape) { + PartTreeJpaQuery(JpaQueryMethod method, EntityManager em, EscapeCharacter escape, boolean validate) { super(method, em); @@ -99,7 +99,9 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { try { this.tree = new PartTree(method.getName(), domainClass); - validate(tree, parameters, method.toString()); + if(validate) { + validate(tree, parameters, method.toString()); + } this.countQuery = new CountQueryPreparer(recreationRequired); this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(recreationRequired); 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 16fa3c30e0..93fee1df44 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 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; +import org.springframework.core.SpringProperties; import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ValueExpressionDelegate; @@ -45,8 +46,8 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { * @param valueExpressionDelegate must not be {@literal null} */ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String countQueryString, - QueryRewriter queryRewriter, ValueExpressionDelegate valueExpressionDelegate) { - this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, queryRewriter, valueExpressionDelegate); + QueryRewriter queryRewriter, ValueExpressionDelegate valueExpressionDelegate, boolean validation) { + this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, queryRewriter, valueExpressionDelegate, validation); } /** @@ -60,15 +61,17 @@ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String * @param valueExpressionDelegate must not be {@literal null} */ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, QueryRewriter queryRewriter, - ValueExpressionDelegate valueExpressionDelegate) { + ValueExpressionDelegate valueExpressionDelegate, boolean validation) { super(method, em, queryString, countQueryString, queryRewriter, valueExpressionDelegate); - validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s", method); + if(validation) { + validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s", method); - if (method.isPageQuery()) { - validateQuery(getCountQuery().getQueryString(), + if (method.isPageQuery()) { + validateQuery(getCountQuery().getQueryString(), String.format("Count query validation failed for method %s", method)); + } } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java index e6e51b991d..69ec8f117d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java @@ -36,6 +36,7 @@ public class DefaultJpaContext implements JpaContext { private final MultiValueMap, EntityManager> entityManagers; + private boolean validateQueries; /** * Creates a new {@link DefaultJpaContext} for the given {@link Set} of {@link EntityManager}s. 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 7ecd163244..a882c16f23 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 @@ -31,6 +31,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.core.SpringProperties; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.jpa.projection.CollectionAwareProjectionFactory; import org.springframework.data.jpa.provider.PersistenceProvider; @@ -90,6 +91,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT; private JpaQueryMethodFactory queryMethodFactory; private QueryRewriterProvider queryRewriterProvider; + private boolean queryValidation; /** * Creates a new {@link JpaRepositoryFactory}. @@ -120,6 +122,8 @@ public JpaRepositoryFactory(EntityManager entityManager) { } this.crudMethodMetadata = crudMethodMetadataPostProcessor.getCrudMethodMetadata(); + String p = SpringProperties.getProperty("spring.data.jpa.query.validate"); + this.queryValidation = p == null || Boolean.parseBoolean(p); } @Override @@ -167,6 +171,15 @@ public void setEscapeCharacter(EscapeCharacter escapeCharacter) { this.escapeCharacter = escapeCharacter; } + /** + * Configures if spring based queries should be validated on creation + * + * @param queryValidation a character used for escaping in certain like expressions. + * @since 3.4 + */ + public void setQueryValidation(boolean queryValidation) { + this.queryValidation = queryValidation; + } /** * Configures the {@link JpaQueryMethodFactory} to be used. Defaults to {@link DefaultJpaQueryMethodFactory}. * @@ -240,7 +253,7 @@ protected Optional getQueryLookupStrategy(@Nullable Key key ValueExpressionDelegate valueExpressionDelegate) { return Optional.of(JpaQueryLookupStrategy.create(entityManager, queryMethodFactory, key, new CachingValueExpressionDelegate(valueExpressionDelegate), - queryRewriterProvider, escapeCharacter)); + queryRewriterProvider, escapeCharacter, queryValidation)); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index f75f45a8d1..733a14529f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -48,6 +48,7 @@ public class JpaRepositoryFactoryBean, S, ID> private @Nullable EntityManager entityManager; private EntityPathResolver entityPathResolver; private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT; + private Boolean queryValidation; private JpaQueryMethodFactory queryMethodFactory; /** @@ -116,6 +117,9 @@ protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityM JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager); jpaRepositoryFactory.setEntityPathResolver(entityPathResolver); jpaRepositoryFactory.setEscapeCharacter(escapeCharacter); + if(queryValidation != null) { + jpaRepositoryFactory.setQueryValidation(queryValidation); + } if (queryMethodFactory != null) { jpaRepositoryFactory.setQueryMethodFactory(queryMethodFactory); @@ -136,4 +140,12 @@ public void setEscapeCharacter(char escapeCharacter) { this.escapeCharacter = EscapeCharacter.of(escapeCharacter); } + + /** + * @param queryValidation + * @since 3.4 + */ + public void setQueryValidation(Boolean queryValidation) { + this.queryValidation = queryValidation; + } } 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 9cb74dcaa0..aa4c52df86 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 @@ -67,7 +67,7 @@ void createsNormalQueryForJpaManagedReturnTypes() throws Exception { JpaQueryMethod method = getMethod("findRolesByEmailAddress", String.class); AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, null, QueryRewriter.IdentityQueryRewriter.INSTANCE, - ValueExpressionDelegate.create()); + ValueExpressionDelegate.create(), true); 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/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index e5a6b04334..13bfaef30d 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 @@ -120,7 +120,7 @@ void prefersDeclaredCountQueryOverCreatingOne() throws Exception { when(em.createQuery("foo", Long.class)).thenReturn(typedQuery); SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", null, - QueryRewriter.IdentityQueryRewriter.INSTANCE, ValueExpressionDelegate.create()); + QueryRewriter.IdentityQueryRewriter.INSTANCE, ValueExpressionDelegate.create(), true); assertThat(jpaQuery.createCountQuery(new JpaParametersParameterAccessor(method.getParameters(), new Object[] {}))) .isEqualTo(typedQuery); @@ -135,7 +135,7 @@ void doesNotApplyPaginationToCountQuery() throws Exception { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", null, - QueryRewriter.IdentityQueryRewriter.INSTANCE, ValueExpressionDelegate.create()); + QueryRewriter.IdentityQueryRewriter.INSTANCE, ValueExpressionDelegate.create(), true); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -151,7 +151,7 @@ void discoversNativeQuery() throws Exception { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, queryMethod.getAnnotatedQuery(), null, QueryRewriter.IdentityQueryRewriter.INSTANCE, - ValueExpressionDelegate.create()); + ValueExpressionDelegate.create(), true); assertThat(jpaQuery).isInstanceOf(NativeJpaQuery.class); @@ -171,7 +171,7 @@ void discoversNativeQueryFromNativeQueryInterface() throws Exception { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, queryMethod.getAnnotatedQuery(), null, QueryRewriter.IdentityQueryRewriter.INSTANCE, - ValueExpressionDelegate.create()); + ValueExpressionDelegate.create(), true); assertThat(jpaQuery).isInstanceOf(NativeJpaQuery.class); @@ -205,6 +205,16 @@ void validatesAndRejectsCountQueryIfPagingMethod() throws Exception { .withMessageContaining(method.getName()); } + @Test // GH-1690 + void skipsValidationIfFlagSetToFalse() throws Exception { + + Method method = SampleRepository.class.getMethod("pageByAnnotatedQuery", Pageable.class); + + when(em.createQuery(Mockito.contains("count"))).thenThrow(IllegalArgumentException.class); + + assertThatNoException().isThrownBy(() -> createJpaQuery(method, false)); + } + @Test void createsASimpleJpaQueryFromAnnotation() throws Exception { @@ -284,7 +294,7 @@ void resolvesExpressionInCountQuery() throws Exception { AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", "select count(u.id) from #{#entityName} u", QueryRewriter.IdentityQueryRewriter.INSTANCE, - ValueExpressionDelegate.create()); + ValueExpressionDelegate.create(), true); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -292,20 +302,28 @@ void resolvesExpressionInCountQuery() throws Exception { verify(em).createQuery(eq("select count(u.id) from User u"), eq(Long.class)); } + private AbstractJpaQuery createJpaQuery(Method method, boolean validate) { + return createJpaQuery(method, null, validate); + } + private AbstractJpaQuery createJpaQuery(Method method) { - return createJpaQuery(method, null); + return createJpaQuery(method, true); } - private AbstractJpaQuery createJpaQuery(JpaQueryMethod queryMethod, @Nullable String queryString, @Nullable String countQueryString) { + private AbstractJpaQuery createJpaQuery(JpaQueryMethod queryMethod, @Nullable String queryString, @Nullable String countQueryString, boolean validate) { return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, queryString, countQueryString, - QueryRewriter.IdentityQueryRewriter.INSTANCE, ValueExpressionDelegate.create()); + QueryRewriter.IdentityQueryRewriter.INSTANCE, ValueExpressionDelegate.create(), validate); } private AbstractJpaQuery createJpaQuery(Method method, @Nullable Optional countQueryString) { + return createJpaQuery(method, countQueryString, true); + } + + private AbstractJpaQuery createJpaQuery(Method method, @Nullable Optional countQueryString, boolean validate) { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); - return createJpaQuery(queryMethod, queryMethod.getAnnotatedQuery(), countQueryString == null ? null : countQueryString.orElse(queryMethod.getCountQuery())); + return createJpaQuery(queryMethod, queryMethod.getAnnotatedQuery(), countQueryString == null ? null : countQueryString.orElse(queryMethod.getCountQuery()), validate); } interface SampleRepository {