From fb0aeb0a36e843c88c6d4785b745ea4745eb8dda Mon Sep 17 00:00:00 2001 From: Tom Hombergs Date: Sat, 30 Mar 2019 06:47:49 +0100 Subject: [PATCH] paging examples --- .../pagination/MovieCharacter.java | 2 + .../pagination/MovieCharacterRepository.java | 17 +++++- .../pagination/PaginatedController.java | 40 +++++++++----- .../pagination/PaginationApplication.java | 2 + .../pagination/PaginationConfiguration.java | 9 ---- .../src/main/resources/application.properties | 3 +- .../pagination/PaginatedControllerTest.java | 52 +++++++++++++++++- ...trollerWithCustomPagingPropertiesTest.java | 54 +++++++++++++++++++ 8 files changed, 153 insertions(+), 26 deletions(-) delete mode 100644 spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginationConfiguration.java create mode 100644 spring-boot/pagination/src/test/java/io/reflectoring/pagination/PaginatedControllerWithCustomPagingPropertiesTest.java diff --git a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/MovieCharacter.java b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/MovieCharacter.java index 3e0525f..51fde14 100644 --- a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/MovieCharacter.java +++ b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/MovieCharacter.java @@ -5,9 +5,11 @@ import lombok.Data; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Table; @Entity @Data +@Table(name = "character") class MovieCharacter { @Id diff --git a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/MovieCharacterRepository.java b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/MovieCharacterRepository.java index 97477b6..05ff380 100644 --- a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/MovieCharacterRepository.java +++ b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/MovieCharacterRepository.java @@ -6,14 +6,29 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; -interface MovieCharacterRepository extends CrudRepository { +interface MovieCharacterRepository + extends CrudRepository { + @Query("select c from MovieCharacter c") Page findAllPage(Pageable pageable); + @Query("select c from MovieCharacter c") Slice findAllSlice(Pageable pageable); + @Query("select c from MovieCharacter c") List findAllSorted(Sort sort); + Page findByMovie(String movieName, Pageable pageable); + + @Query("select c from MovieCharacter c where c.movie = :movie") + Slice findByMovieCustom( + @Param("movie") String movieName, Pageable pageable); + + @Query("select c from MovieCharacter c where c.movie = :movie") + List findByMovieSorted( + @Param("movie") String movieName, Sort sort); } diff --git a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginatedController.java b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginatedController.java index c82db15..4a10643 100644 --- a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginatedController.java +++ b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginatedController.java @@ -3,10 +3,13 @@ package io.reflectoring.pagination; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.data.web.SortDefault; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -14,20 +17,31 @@ import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor class PaginatedController { - private final MovieCharacterRepository characterRepository; + private final MovieCharacterRepository characterRepository; - @GetMapping(path = "/characters/page") - Page loadCharactersPage(Pageable pageable) { - return characterRepository.findAllPage(pageable); - } + @GetMapping(path = "/characters/page") + Page loadCharactersPage( + @PageableDefault(page = 0, size = 20) + @SortDefault.SortDefaults({ + @SortDefault(sort = "name", direction = Sort.Direction.DESC), + @SortDefault(sort = "id", direction = Sort.Direction.ASC) + }) Pageable pageable) { + return characterRepository.findAllPage(pageable); + } - @GetMapping(path = "/characters/sorted") - List loadCharactersSorted(Sort sort) { - return characterRepository.findAllSorted(sort); - } + @GetMapping(path = "/characters/qualifier") + Page loadCharactersPageWithQualifier( + @Qualifier("my") Pageable pageable) { + return characterRepository.findAllPage(pageable); + } - @GetMapping(path = "/characters/slice") - Slice loadCharactersSlice(Pageable pageable) { - return characterRepository.findAllSlice(pageable); - } + @GetMapping(path = "/characters/sorted") + List loadCharactersSorted(Sort sort) { + return characterRepository.findAllSorted(sort); + } + + @GetMapping(path = "/characters/slice") + Slice loadCharactersSlice(Pageable pageable) { + return characterRepository.findAllSlice(pageable); + } } diff --git a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginationApplication.java b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginationApplication.java index ced2210..b1fc9e0 100644 --- a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginationApplication.java +++ b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginationApplication.java @@ -1,7 +1,9 @@ package io.reflectoring.pagination; import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration; @SpringBootApplication public class PaginationApplication { diff --git a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginationConfiguration.java b/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginationConfiguration.java deleted file mode 100644 index 3c6aecb..0000000 --- a/spring-boot/pagination/src/main/java/io/reflectoring/pagination/PaginationConfiguration.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.reflectoring.pagination; - -import org.springframework.context.annotation.Configuration; -import org.springframework.data.web.config.EnableSpringDataWebSupport; - -@Configuration -@EnableSpringDataWebSupport -class PaginationConfiguration { -} diff --git a/spring-boot/pagination/src/main/resources/application.properties b/spring-boot/pagination/src/main/resources/application.properties index 0cc2766..1d4466f 100644 --- a/spring-boot/pagination/src/main/resources/application.properties +++ b/spring-boot/pagination/src/main/resources/application.properties @@ -2,6 +2,5 @@ spring.data.web.pageable.size-parameter=size spring.data.web.pageable.page-parameter=page spring.data.web.pageable.default-page-size=10 spring.data.web.pageable.one-indexed-parameters=false -spring.data.web.pageable.max-page-size=10000 -spring.data.web.pageable.prefix= +spring.data.web.pageable.prefix=2000 spring.data.web.pageable.qualifier-delimiter=_ diff --git a/spring-boot/pagination/src/test/java/io/reflectoring/pagination/PaginatedControllerTest.java b/spring-boot/pagination/src/test/java/io/reflectoring/pagination/PaginatedControllerTest.java index 405f6f2..3c1512b 100644 --- a/spring-boot/pagination/src/test/java/io/reflectoring/pagination/PaginatedControllerTest.java +++ b/spring-boot/pagination/src/test/java/io/reflectoring/pagination/PaginatedControllerTest.java @@ -36,7 +36,7 @@ class PaginatedControllerTest { mockMvc.perform(get("/characters/page") .param("page", "5") .param("size", "10") - .param("sort", "id, desc") // <-- no space after comma!!! + .param("sort", "id,desc") // <-- no space after comma!!! .param("sort", "name,asc")) // <-- no space after comma!!! .andExpect(status().isOk()); @@ -50,6 +50,56 @@ class PaginatedControllerTest { assertThat(pageable).hasSort("id", Sort.Direction.DESC); } + @Test + void evaluatesQualifier() throws Exception { + + mockMvc.perform(get("/characters/qualifier") + .param("my_page", "5") + .param("my_size", "10") + .param("my_sort", "id,desc") // <-- no space after comma!!! + .param("my_sort", "name,asc")) // <-- no space after comma!!! + .andExpect(status().isOk()); + + ArgumentCaptor pageableCaptor = ArgumentCaptor.forClass(Pageable.class); + verify(characterRepository).findAllPage(pageableCaptor.capture()); + PageRequest pageable = (PageRequest) pageableCaptor.getValue(); + + assertThat(pageable).hasPageNumber(5); + assertThat(pageable).hasPageSize(10); + assertThat(pageable).hasSort("name", Sort.Direction.ASC); + assertThat(pageable).hasSort("id", Sort.Direction.DESC); + } + + @Test + void setsUpperPageLimit() throws Exception { + + mockMvc.perform(get("/characters/page") + .param("size", "10000")) + .andExpect(status().isOk()); + + ArgumentCaptor pageableCaptor = ArgumentCaptor.forClass(Pageable.class); + verify(characterRepository).findAllPage(pageableCaptor.capture()); + PageRequest pageable = (PageRequest) pageableCaptor.getValue(); + + assertThat(pageable).hasPageSize(2000); + } + + @Test + void evaluatesPageableDefault() throws Exception { + + mockMvc.perform(get("/characters/page")) + .andExpect(status().isOk()); + + ArgumentCaptor pageableCaptor = ArgumentCaptor.forClass(Pageable.class); + verify(characterRepository).findAllPage(pageableCaptor.capture()); + PageRequest pageable = (PageRequest) pageableCaptor.getValue(); + + assertThat(pageable).hasPageNumber(0); + assertThat(pageable).hasPageSize(20); + assertThat(pageable).hasSort("name", Sort.Direction.DESC); + assertThat(pageable).hasSort("id", Sort.Direction.ASC); + } + @Test void returnsSlice() throws Exception { diff --git a/spring-boot/pagination/src/test/java/io/reflectoring/pagination/PaginatedControllerWithCustomPagingPropertiesTest.java b/spring-boot/pagination/src/test/java/io/reflectoring/pagination/PaginatedControllerWithCustomPagingPropertiesTest.java new file mode 100644 index 0000000..1e6f3fe --- /dev/null +++ b/spring-boot/pagination/src/test/java/io/reflectoring/pagination/PaginatedControllerWithCustomPagingPropertiesTest.java @@ -0,0 +1,54 @@ +package io.reflectoring.pagination; + +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.servlet.MockMvc; +import static io.reflectoring.pagination.PageableAssert.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(controllers = PaginatedController.class) +@TestPropertySource(properties = { + "spring.data.web.pageable.prefix=prefix_", + "spring.data.web.pageable.size-parameter=my-size", + "spring.data.web.pageable.page-parameter=my-page" +}) +class PaginatedControllerWithCustomPagingPropertiesTest { + + @MockBean + private MovieCharacterRepository characterRepository; + + @Autowired + private MockMvc mockMvc; + + @Test + void evaluatesPageableParameter() throws Exception { + + mockMvc.perform(get("/characters/page") + .param("prefix_my-page", "5") + .param("prefix_my-size", "10") + .param("sort", "id,desc") // <-- no space after comma!!! + .param("sort", "name,asc")) // <-- no space after comma!!! + .andExpect(status().isOk()); + + ArgumentCaptor pageableCaptor = ArgumentCaptor.forClass(Pageable.class); + verify(characterRepository).findAllPage(pageableCaptor.capture()); + PageRequest pageable = (PageRequest) pageableCaptor.getValue(); + + assertThat(pageable).hasPageNumber(5); + assertThat(pageable).hasPageSize(10); + assertThat(pageable).hasSort("name", Sort.Direction.ASC); + assertThat(pageable).hasSort("id", Sort.Direction.DESC); + } + +}