From 256bef652261ac409d12b38fa00d7fad21aa9a69 Mon Sep 17 00:00:00 2001 From: radovanradic Date: Fri, 25 Nov 2022 11:31:51 +0100 Subject: [PATCH 1/3] Fix count query with different parameters than main query --- .../data/intercept/annotation/DataMethod.java | 7 ++++++- .../RepositoryTypeElementVisitor.java | 19 +++++++++++++------ .../query/internal/DefaultStoredQuery.java | 15 +++++++++++++-- .../data/tck/tests/AbstractPageSpec.groovy | 18 ++++++++++++++++++ .../tck/repositories/PersonRepository.java | 4 ++++ 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/data-model/src/main/java/io/micronaut/data/intercept/annotation/DataMethod.java b/data-model/src/main/java/io/micronaut/data/intercept/annotation/DataMethod.java index 01c3bb8dfe..eb4888c51c 100644 --- a/data-model/src/main/java/io/micronaut/data/intercept/annotation/DataMethod.java +++ b/data-model/src/main/java/io/micronaut/data/intercept/annotation/DataMethod.java @@ -150,10 +150,15 @@ String META_MEMBER_RAW_QUERY = "rawQuery"; /** - * Whether the user is a raw user specified query. + * Whether the user declared raw count query. */ String META_MEMBER_RAW_COUNT_QUERY = "rawCountQuery"; + /** + * Used when there is a raw count query declared. + */ + String META_MEMBER_RAW_COUNT_PARAMETERS = "rawCountParameters"; + /** * Meta member for storing the parameter type defs. */ diff --git a/data-processor/src/main/java/io/micronaut/data/processor/visitors/RepositoryTypeElementVisitor.java b/data-processor/src/main/java/io/micronaut/data/processor/visitors/RepositoryTypeElementVisitor.java index 40346b700d..39444ab394 100644 --- a/data-processor/src/main/java/io/micronaut/data/processor/visitors/RepositoryTypeElementVisitor.java +++ b/data-processor/src/main/java/io/micronaut/data/processor/visitors/RepositoryTypeElementVisitor.java @@ -308,7 +308,7 @@ private void processMethodInfo(MethodMatchContext methodMatchContext, MethodMatc // no need to annotation since already annotated, just replace the // the computed parameter names parameterBinding = queryResult.getParameterBindings(); - + encodeEntityParameters = methodInfo.isEncodeEntityParameters(); element.annotate(Query.class, (builder) -> builder.member(DataMethod.META_MEMBER_RAW_QUERY, element.stringValue(Query.class) .map(q -> addRawQueryParameterPlaceholders(queryEncoder, queryResult.getQuery(), queryResult.getQueryParts())) @@ -324,10 +324,16 @@ private void processMethodInfo(MethodMatchContext methodMatchContext, MethodMatc Query.class, (builder) -> builder.member(DataMethod.META_MEMBER_RAW_COUNT_QUERY, addRawQueryParameterPlaceholders(queryEncoder, countQueryResult.getQuery(), countQueryResult.getQueryParts())) ); + // For count query, we might need to take parameters for that query + // since it can differ from the main query + final List finalCountParameterBindings = countQueryResult.getParameterBindings(); + if (CollectionUtils.isNotEmpty(finalCountParameterBindings)) { + final boolean finalEncodeEntityParameters = encodeEntityParameters; + element.annotate(DataMethod.class, (builder) -> bindParameters(supportsImplicitQueries, finalCountParameterBindings, finalEncodeEntityParameters, + builder, DataMethod.META_MEMBER_RAW_COUNT_PARAMETERS)); + } } } - - encodeEntityParameters = methodInfo.isEncodeEntityParameters(); } else { encodeEntityParameters = methodInfo.isEncodeEntityParameters(); @@ -429,7 +435,7 @@ private void processMethodInfo(MethodMatchContext methodMatchContext, MethodMatc .ifPresent(parameterElement -> annotationBuilder.member(DataMethod.META_MEMBER_ENTITY, parameterElement.getName())); if (CollectionUtils.isNotEmpty(finalParameterBinding)) { - bindParameters(supportsImplicitQueries, finalParameterBinding, finalEncodeEntityParameters, annotationBuilder); + bindParameters(supportsImplicitQueries, finalParameterBinding, finalEncodeEntityParameters, annotationBuilder, DataMethod.META_MEMBER_PARAMETERS); } }); @@ -438,7 +444,8 @@ private void processMethodInfo(MethodMatchContext methodMatchContext, MethodMatc private void bindParameters(boolean supportsImplicitQueries, List finalParameterBinding, boolean finalEncodeEntityParameters, - AnnotationValueBuilder annotationBuilder) { + AnnotationValueBuilder annotationBuilder, + String parametersMemberName) { List> annotationValues = new ArrayList<>(); for (QueryParameterBinding p : finalParameterBinding) { @@ -477,7 +484,7 @@ private void bindParameters(boolean supportsImplicitQueries, annotationValues.add(builder.build()); } AnnotationValue[] annotations = annotationValues.toArray(new AnnotationValue[0]); - annotationBuilder.member(DataMethod.META_MEMBER_PARAMETERS, annotations); + annotationBuilder.member(parametersMemberName, annotations); } private void bindAdditionalParameters(MatchContext matchContext, diff --git a/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java b/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java index e9de427ced..a289544b6b 100644 --- a/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java +++ b/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java @@ -20,6 +20,7 @@ import io.micronaut.core.annotation.Internal; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.reflect.ReflectionUtils; +import io.micronaut.core.util.CollectionUtils; import io.micronaut.core.util.StringUtils; import io.micronaut.data.annotation.Query; import io.micronaut.data.annotation.QueryHint; @@ -79,7 +80,7 @@ public final class DefaultStoredQuery extends DefaultStoredDataOperation< private Set joinFetchPaths = null; private final List queryParameters; private final boolean rawQuery; - + /** * The default constructor. * @@ -158,7 +159,17 @@ public DefaultStoredQuery( if (annotation == null) { queryParameters = Collections.emptyList(); } else { - List> params = annotation.getAnnotations(DataMethod.META_MEMBER_PARAMETERS, DataMethodQueryParameter.class); + List> params = null; + if (isCount) { + // Count query might have different parameters from the main query, so we use them here if present + List> countParams = annotation.getAnnotations(DataMethod.META_MEMBER_RAW_COUNT_PARAMETERS, DataMethodQueryParameter.class); + if (CollectionUtils.isNotEmpty(countParams)) { + params = countParams; + } + } + if (params == null) { + params = annotation.getAnnotations(DataMethod.META_MEMBER_PARAMETERS, DataMethodQueryParameter.class); + } List queryParameters = new ArrayList<>(params.size()); for (AnnotationValue av : params) { String[] propertyPath = av.stringValues(DataMethodQueryParameter.META_MEMBER_PROPERTY_PATH); diff --git a/data-tck/src/main/groovy/io/micronaut/data/tck/tests/AbstractPageSpec.groovy b/data-tck/src/main/groovy/io/micronaut/data/tck/tests/AbstractPageSpec.groovy index c1762444bc..3264c56f6e 100644 --- a/data-tck/src/main/groovy/io/micronaut/data/tck/tests/AbstractPageSpec.groovy +++ b/data-tck/src/main/groovy/io/micronaut/data/tck/tests/AbstractPageSpec.groovy @@ -183,4 +183,22 @@ abstract class AbstractPageSpec extends Specification { cleanup: bookRepository.deleteAll() } + + void "test pageable findBy and count using different params"() { + given: + def person1 = new Person(name: "John") + def person2 = new Person(name: "Jonas", enabled: false) + def person3 = new Person(name: "Joanna", enabled: false) + personRepository.saveAll(Arrays.asList(person1, person2, person3)) + when: "People are searched by name and enabled" + def pageable = Pageable.from(0, 10) + Page page = personRepository.findByNameLikeAndEnabled("Jo%", true, pageable) + + then: "The page returns correct results and count" + page.offset == 0 + page.pageNumber == 0 + page.totalSize == 3 + page.content + page.content.size() == 1 + } } diff --git a/data-tck/src/main/java/io/micronaut/data/tck/repositories/PersonRepository.java b/data-tck/src/main/java/io/micronaut/data/tck/repositories/PersonRepository.java index d9cd8babe6..1002c1cf05 100644 --- a/data-tck/src/main/java/io/micronaut/data/tck/repositories/PersonRepository.java +++ b/data-tck/src/main/java/io/micronaut/data/tck/repositories/PersonRepository.java @@ -106,6 +106,10 @@ public interface PersonRepository extends CrudRepository, Pageable Page findByNameLike(String name, Pageable pageable); + @Query(value = "select * from person person_ where person_.name like :name AND enabled = :enabled", + countQuery = "select count(*) from person person_ where person_.name like :name") + Page findByNameLikeAndEnabled(String name, boolean enabled, Pageable pageable); + List listTop10(Sort sort); Slice find(Pageable pageable); From 9fc34ef74adc87c6517d799c3417e4fd7287066b Mon Sep 17 00:00:00 2001 From: radovanradic Date: Fri, 25 Nov 2022 11:58:12 +0100 Subject: [PATCH 2/3] Resolve Sonar warning --- .../data/processor/visitors/RepositoryTypeElementVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-processor/src/main/java/io/micronaut/data/processor/visitors/RepositoryTypeElementVisitor.java b/data-processor/src/main/java/io/micronaut/data/processor/visitors/RepositoryTypeElementVisitor.java index 39444ab394..16b2d54bff 100644 --- a/data-processor/src/main/java/io/micronaut/data/processor/visitors/RepositoryTypeElementVisitor.java +++ b/data-processor/src/main/java/io/micronaut/data/processor/visitors/RepositoryTypeElementVisitor.java @@ -329,7 +329,7 @@ private void processMethodInfo(MethodMatchContext methodMatchContext, MethodMatc final List finalCountParameterBindings = countQueryResult.getParameterBindings(); if (CollectionUtils.isNotEmpty(finalCountParameterBindings)) { final boolean finalEncodeEntityParameters = encodeEntityParameters; - element.annotate(DataMethod.class, (builder) -> bindParameters(supportsImplicitQueries, finalCountParameterBindings, finalEncodeEntityParameters, + element.annotate(DataMethod.class, builder -> bindParameters(supportsImplicitQueries, finalCountParameterBindings, finalEncodeEntityParameters, builder, DataMethod.META_MEMBER_RAW_COUNT_PARAMETERS)); } } From b265f439c503cf804e9c2b39e30698ece070224f Mon Sep 17 00:00:00 2001 From: radovanradic Date: Fri, 25 Nov 2022 12:22:14 +0100 Subject: [PATCH 3/3] Formatting --- .../data/runtime/query/internal/DefaultStoredQuery.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java b/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java index a289544b6b..7c376d6753 100644 --- a/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java +++ b/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java @@ -80,7 +80,7 @@ public final class DefaultStoredQuery extends DefaultStoredDataOperation< private Set joinFetchPaths = null; private final List queryParameters; private final boolean rawQuery; - + /** * The default constructor. *