From 133ec27b74e58dce4aa59b201bd84fe9aeb30a6b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 5 Jul 2023 11:25:02 +0200 Subject: [PATCH] Add support for `Limit`. Closes #3052 --- .../jpa/repository/query/JpaParameters.java | 11 ++++++++-- .../data/jpa/repository/query/NamedQuery.java | 2 +- .../jpa/repository/query/ParameterBinder.java | 2 +- .../repository/UserRepositoryFinderTests.java | 21 +++++++++++++++++++ .../query/JpaQueryMethodUnitTests.java | 14 ++++++------- .../jpa/repository/sample/UserRepository.java | 3 +++ 6 files changed, 42 insertions(+), 11 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index 428c0ec18..b41144a3f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -15,12 +15,12 @@ */ package org.springframework.data.jpa.repository.query; +import jakarta.persistence.TemporalType; + import java.lang.reflect.Method; import java.util.Date; import java.util.List; -import jakarta.persistence.TemporalType; - import org.springframework.core.MethodParameter; import org.springframework.data.jpa.repository.Temporal; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; @@ -60,6 +60,13 @@ public class JpaParameters extends Parameters { return new JpaParameters(parameters); } + /** + * @return {@code true} if the method signature declares Limit or Pageable parameters. + */ + public boolean hasLimitingParameters() { + return hasLimitParameter() || hasPageableParameter(); + } + /** * Custom {@link Parameter} implementation adding parameters of type {@link Temporal} to the special ones. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index e78c9c253..404a0673a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -79,7 +79,7 @@ final class NamedQuery extends AbstractJpaQuery { this.declaredQuery = DeclaredQuery.of(queryString, false); - boolean weNeedToCreateCountQuery = !namedCountQueryIsPresent && method.getParameters().hasPageableParameter(); + boolean weNeedToCreateCountQuery = !namedCountQueryIsPresent && method.getParameters().hasLimitingParameters(); boolean cantExtractQuery = !extractor.canExtractQuery(); if (weNeedToCreateCountQuery && cantExtractQuery) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 9298308d6..4c30d2fd4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -96,7 +96,7 @@ public class ParameterBinder { bind(query, metadata, accessor); - if (!useJpaForPaging || !parameters.hasPageableParameter() || accessor.getPageable().isUnpaged()) { + if (!useJpaForPaging || !parameters.hasLimitingParameters() || accessor.getPageable().isUnpaged()) { return query; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 5ded8dd6b..00dd6c2dd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -29,11 +29,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Limit; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; @@ -53,6 +56,7 @@ import org.springframework.transaction.annotation.Transactional; * @author Oliver Gierke * @author Krzysztof Krason * @author Greg Turnquist + * @author Mark Paluch * @see QueryLookupStrategy */ @ExtendWith(SpringExtension.class) @@ -221,6 +225,23 @@ class UserRepositoryFinderTests { assertThat(slice.hasNext()).isFalse(); } + @Test // DATAJPA-94 + void executesQueryWithLimitAndScrollPosition() { + + Window first = userRepository.findByLastnameOrderByFirstname(Limit.of(1), // + ScrollPosition.offset(), // + "Matthews" // + ); + + Window next = userRepository.findByLastnameOrderByFirstname(Limit.of(1), // + ScrollPosition.offset(1), // + "Matthews" // + ); + + assertThat(first).containsExactly(dave); + assertThat(next).containsExactly(oliver); + } + @Test // DATAJPA-830 void executesMethodWithNotContainingOnStringCorrectly() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 3854b4998..91b92738d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -15,13 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import jakarta.persistence.LockModeType; import jakarta.persistence.QueryHint; @@ -159,6 +155,8 @@ class JpaQueryMethodUnitTests { @Test void rejectsInvalidReturntypeOnPagebleFinder() { + when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + assertThatIllegalStateException() .isThrownBy(() -> new JpaQueryMethod(invalidReturnType, metadata, factory, extractor)); } @@ -166,6 +164,8 @@ class JpaQueryMethodUnitTests { @Test void rejectsPageableAndSortInFinderMethod() { + when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + assertThatIllegalStateException() .isThrownBy(() -> new JpaQueryMethod(pageableAndSort, metadata, factory, extractor)); } 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 fad09d8a2..1b2870316 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 @@ -26,6 +26,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Stream; +import org.springframework.data.domain.Limit; import org.springframework.data.domain.OffsetScrollPosition; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -226,6 +227,8 @@ public interface UserRepository extends JpaRepository, JpaSpecifi Page findByLastnameIgnoringCase(Pageable pageable, String lastname); + Window findByLastnameOrderByFirstname(Limit limit, ScrollPosition scrollPosition, String lastname); + List findByLastnameIgnoringCaseLike(String lastname); List findByLastnameAndFirstnameAllIgnoringCase(String lastname, String firstname);