From 43d035fe545e8ec1f82b5b3cafeca819423feeec Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Thu, 5 Jun 2014 14:41:47 +0200 Subject: [PATCH] DATAJPA-551 - Add support for limiting the query result in the query derivation mechanism. We now support to restrict the results of a query method via the "Top" or "First" keyword within the method name. We adjust the firstResult offset of the query in case we detect a tighter bound on the result size limit form the derived query method that is smaller than the page size of the given Pageable. Added test cases for Paged and Sliced query executions. Original pull request: #96. --- .../repository/query/PartTreeJpaQuery.java | 32 ++++- .../jpa/repository/UserRepositoryTests.java | 135 ++++++++++++++++++ .../jpa/repository/sample/UserRepository.java | 65 +++++++++ 3 files changed, 231 insertions(+), 1 deletion(-) 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); }