diff --git a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index befa4abd9..0029903ac 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -128,7 +128,37 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { } TypedQuery jpaQuery = createQuery(criteriaQuery); - return invokeBinding(getBinder(values, expressions), jpaQuery); + + return restrictMaxResultsIfNecessary(invokeBinding(getBinder(values, expressions), jpaQuery)); + } + + /** + * Restricts the max results of the given {@link Query} if the current {@code tree} marks this {@code query} as + * limited. + * + * @param query + * @return + */ + private Query restrictMaxResultsIfNecessary(Query query) { + + if (tree.isLimiting()) { + + if (query.getMaxResults() != Integer.MAX_VALUE) { + /* + * In order to return the correct results, we have to adjust the first result offset to be returned if: + * - a Pageable parameter is present + * - AND the requested page number > 0 + * - AND the requested page size was bigger than the derived result limitation via the First/Top keyword. + */ + if (query.getMaxResults() > tree.getMaxResults() && query.getFirstResult() > 0) { + query.setFirstResult(query.getFirstResult() - (query.getMaxResults() - tree.getMaxResults())); + } + } + + query.setMaxResults(tree.getMaxResults()); + } + + return query; } /** diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index f17191753..2cd92db3b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -49,6 +49,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; @@ -1421,6 +1422,140 @@ public class UserRepositoryTests { assertThat(result.getContent().size(), is(1)); } + /** + * @see DATAJPA-551 + */ + @Test + public void findOldestUser() { + + flushTestUsers(); + + User oldest = thirdUser; + + assertThat(repository.findFirstByOrderByAgeDesc(), is(oldest)); + assertThat(repository.findFirst1ByOrderByAgeDesc(), is(oldest)); + } + + /** + * @see DATAJPA-551 + */ + @Test + public void findYoungestUser() { + + flushTestUsers(); + + User youngest = firstUser; + + assertThat(repository.findTopByOrderByAgeAsc(), is(youngest)); + assertThat(repository.findTop1ByOrderByAgeAsc(), is(youngest)); + } + + /** + * @see DATAJPA-551 + */ + @Test + public void find2OldestUsers() { + + flushTestUsers(); + + User oldest1 = thirdUser; + User oldest2 = secondUser; + + assertThat(repository.findFirst2ByOrderByAgeDesc(), hasItems(oldest1, oldest2)); + assertThat(repository.findTop2ByOrderByAgeDesc(), hasItems(oldest1, oldest2)); + } + + /** + * @see DATAJPA-551 + */ + @Test + public void find2YoungestUsers() { + + flushTestUsers(); + + User youngest1 = firstUser; + User youngest2 = fourthUser; + + assertThat(repository.findFirst2UsersBy(new Sort(ASC, "age")), hasItems(youngest1, youngest2)); + assertThat(repository.findTop2UsersBy(new Sort(ASC, "age")), hasItems(youngest1, youngest2)); + } + + /** + * @see DATAJPA-551 + */ + @Test + public void find3YoungestUsersPageableWithPageSize2() { + + flushTestUsers(); + + User youngest1 = firstUser; + User youngest2 = fourthUser; + User youngest3 = secondUser; + + Page firstPage = repository.findFirst3UsersBy(new PageRequest(0, 2, ASC, "age")); + assertThat(firstPage.getContent(), hasItems(youngest1, youngest2)); + + Page secondPage = repository.findFirst3UsersBy(new PageRequest(1, 2, ASC, "age")); + assertThat(secondPage.getContent(), hasItems(youngest3)); + } + + /** + * @see DATAJPA-551 + */ + @Test + public void find2YoungestUsersPageableWithPageSize3() { + + flushTestUsers(); + + User youngest1 = firstUser; + User youngest2 = fourthUser; + User youngest3 = secondUser; + + Page firstPage = repository.findFirst2UsersBy(new PageRequest(0, 3, ASC, "age")); + assertThat(firstPage.getContent(), hasItems(youngest1, youngest2)); + + Page secondPage = repository.findFirst2UsersBy(new PageRequest(1, 3, ASC, "age")); + assertThat(secondPage.getContent(), hasItems(youngest3)); + } + + /** + * @see DATAJPA-551 + */ + @Test + public void find3YoungestUsersPageableWithPageSize2Sliced() { + + flushTestUsers(); + + User youngest1 = firstUser; + User youngest2 = fourthUser; + User youngest3 = secondUser; + + Slice firstPage = repository.findTop3UsersBy(new PageRequest(0, 2, ASC, "age")); + assertThat(firstPage.getContent(), hasItems(youngest1, youngest2)); + + Slice secondPage = repository.findTop3UsersBy(new PageRequest(1, 2, ASC, "age")); + assertThat(secondPage.getContent(), hasItems(youngest3)); + } + + /** + * @see DATAJPA-551 + */ + @Test + public void find2YoungestUsersPageableWithPageSize3Sliced() { + + flushTestUsers(); + + User youngest1 = firstUser; + User youngest2 = fourthUser; + User youngest3 = secondUser; + + Slice firstPage = repository.findTop2UsersBy(new PageRequest(0, 3, ASC, "age")); + assertThat(firstPage.getContent(), hasItems(youngest1, youngest2)); + + Slice secondPage = repository.findTop2UsersBy(new PageRequest(1, 3, ASC, "age")); + assertThat(secondPage.getContent(), hasItems(youngest3)); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 2bf94b829..06db46638 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -380,4 +380,69 @@ public interface UserRepository extends JpaRepository, JpaSpecifi */ @Query(name = "User.findBySpringDataNamedQuery", countProjection = "u.firstname") Page findByNamedQueryAndCountProjection(String firstname, Pageable page); + + /** + * @see DATAJPA-551 + */ + User findFirstByOrderByAgeDesc(); + + /** + * @see DATAJPA-551 + */ + User findFirst1ByOrderByAgeDesc(); + + /** + * @see DATAJPA-551 + */ + User findTopByOrderByAgeDesc(); + + /** + * @see DATAJPA-551 + */ + User findTopByOrderByAgeAsc(); + + /** + * @see DATAJPA-551 + */ + User findTop1ByOrderByAgeAsc(); + + /** + * @see DATAJPA-551 + */ + List findTop2ByOrderByAgeDesc(); + + /** + * @see DATAJPA-551 + */ + List findFirst2ByOrderByAgeDesc(); + + /** + * @see DATAJPA-551 + */ + List findFirst2UsersBy(Sort sort); + + /** + * @see DATAJPA-551 + */ + List findTop2UsersBy(Sort sort); + + /** + * @see DATAJPA-551 + */ + Page findFirst3UsersBy(Pageable page); + + /** + * @see DATAJPA-551 + */ + Page findFirst2UsersBy(Pageable page); + + /** + * @see DATAJPA-551 + */ + Slice findTop3UsersBy(Pageable page); + + /** + * @see DATAJPA-551 + */ + Slice findTop2UsersBy(Pageable page); }