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 01c3bb8dfe2..eb4888c51c8 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 40346b700de..16b2d54bff0 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 e9de427ced8..7c376d67537 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; @@ -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 c1762444bc4..3264c56f6eb 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 d9cd8babe6e..1002c1cf05f 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);