From 0f9bb83707b950a826dbea1d46c5b599fec20fdb Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Sun, 28 Apr 2024 15:41:06 +0800 Subject: [PATCH] Ignore offset for `Limit` based query Avoid calling `Query.setFirstResult(0)` which maybe generated sql contains unwanted `offset 0`. Fix GH-3242 --- .../jpa/repository/query/ParameterBinder.java | 7 ++- .../query/ParameterBinderUnitTests.java | 43 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) 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 25f6cff812..dfd0dabcea 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 @@ -30,6 +30,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Jens Schauder + * @author Yanming Zhou */ public class ParameterBinder { @@ -101,7 +102,11 @@ Query bindAndPrepare(Query query, QueryParameterSetter.QueryMetadata metadata, return query; } - query.setFirstResult(PageableUtils.getOffsetAsInteger(accessor.getPageable())); + // see #3242 + if (!parameters.hasLimitParameter()) { + // offset is meaningless if Limit parameter present + query.setFirstResult(PageableUtils.getOffsetAsInteger(accessor.getPageable())); + } query.setMaxResults(accessor.getPageable().getPageSize()); return query; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java index 9a38001018..9fdf725369 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java @@ -41,6 +41,7 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import org.springframework.data.domain.Limit; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.Temporal; @@ -55,6 +56,7 @@ * @author Thomas Darimont * @author Jens Schauder * @author Mark Paluch + * @author Yanming Zhou */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -88,6 +90,8 @@ interface SampleRepository extends Repository { User validWithPageable(@Param("username") String username, Pageable pageable); + User validWithLimit(@Param("username") String username, Limit limit); + User validWithSort(@Param("username") String username, Sort sort); User validWithDefaultTemporalTypeParameter(@Temporal Date registerDate); @@ -124,6 +128,40 @@ void bindWorksWithNullForPageable() throws Exception { verify(query).setParameter(eq(1), eq("foo")); } + @Test + void bindAndPrepareWorksWithPageable() throws Exception { + + Method validWithPageable = SampleRepository.class.getMethod("validWithPageable", String.class, Pageable.class); + + Object[] values = { "foo", Pageable.ofSize(10).withPage(3) }; + bindAndPrepare(validWithPageable, values); + verify(query).setParameter(eq(1), eq("foo")); + verify(query).setFirstResult(eq(30)); + verify(query).setMaxResults(eq(10)); + } + + @Test + void bindWorksWithNullForLimit() throws Exception { + + Method validWithLimit = SampleRepository.class.getMethod("validWithLimit", String.class, Limit.class); + + Object[] values = { "foo", null }; + bind(validWithLimit, values); + verify(query).setParameter(eq(1), eq("foo")); + } + + @Test + void bindAndPrepareWorksWithLimit() throws Exception { + + Method validWithLimit = SampleRepository.class.getMethod("validWithLimit", String.class, Limit.class); + + Object[] values = { "foo", Limit.of(10) }; + bindAndPrepare(validWithLimit, values); + verify(query).setParameter(eq(1), eq("foo")); + verify(query).setMaxResults(eq(10)); + verify(query, never()).setFirstResult(anyInt()); + } + @Test void usesIndexedParametersIfNoParamAnnotationPresent() { @@ -238,6 +276,11 @@ private void bind(Method method, JpaParameters parameters, Object[] values) { getAccessor(method, values), QueryParameterSetter.ErrorHandling.STRICT); } + private void bindAndPrepare(Method method, Object[] values) { + ParameterBinderFactory.createBinder(createParameters(method)).bindAndPrepare(query, + new QueryParameterSetter.QueryMetadata(query), getAccessor(method, values)); + } + private JpaParametersParameterAccessor getAccessor(Method method, Object... values) { return new JpaParametersParameterAccessor(createParameters(method), values); }