From 373a6563494f738a011bb763ab97a8349c84f475 Mon Sep 17 00:00:00 2001 From: haerong22 Date: Wed, 27 Jul 2022 03:34:50 +0900 Subject: [PATCH] #14 simple blog : pageable - querydsl --- simpleblog/build.gradle | 7 ++++ .../simpleblog/config/QueryDslConfig.java | 20 ++++++++++++ .../simpleblog/controller/PostController.java | 7 ++-- .../simpleblog/repository/PostRepository.java | 2 +- .../repository/PostRepositoryCustom.java | 11 +++++++ .../repository/PostRepositoryImpl.java | 27 ++++++++++++++++ .../simpleblog/request/PostSearch.java | 24 ++++++++++++++ .../simpleblog/service/PostService.java | 6 ++-- .../controller/PostControllerTest.java | 32 +++++++++++++++++-- .../simpleblog/service/PostServiceTest.java | 10 +++--- 10 files changed, 132 insertions(+), 14 deletions(-) create mode 100644 simpleblog/src/main/java/com/example/simpleblog/config/QueryDslConfig.java create mode 100644 simpleblog/src/main/java/com/example/simpleblog/repository/PostRepositoryCustom.java create mode 100644 simpleblog/src/main/java/com/example/simpleblog/repository/PostRepositoryImpl.java create mode 100644 simpleblog/src/main/java/com/example/simpleblog/request/PostSearch.java diff --git a/simpleblog/build.gradle b/simpleblog/build.gradle index 0f86b05a..c63ecbc7 100644 --- a/simpleblog/build.gradle +++ b/simpleblog/build.gradle @@ -23,6 +23,13 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'com.querydsl:querydsl-core' + implementation 'com.querydsl:querydsl-jpa' + + annotationProcessor("com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa") + annotationProcessor("jakarta.persistence:jakarta.persistence-api") + annotationProcessor("jakarta.annotation:jakarta.annotation-api") + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' diff --git a/simpleblog/src/main/java/com/example/simpleblog/config/QueryDslConfig.java b/simpleblog/src/main/java/com/example/simpleblog/config/QueryDslConfig.java new file mode 100644 index 00000000..8ce20fd4 --- /dev/null +++ b/simpleblog/src/main/java/com/example/simpleblog/config/QueryDslConfig.java @@ -0,0 +1,20 @@ +package com.example.simpleblog.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +@Configuration +public class QueryDslConfig { + + @PersistenceContext + public EntityManager em; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(em); + } +} diff --git a/simpleblog/src/main/java/com/example/simpleblog/controller/PostController.java b/simpleblog/src/main/java/com/example/simpleblog/controller/PostController.java index 3c8bd943..42f8636f 100644 --- a/simpleblog/src/main/java/com/example/simpleblog/controller/PostController.java +++ b/simpleblog/src/main/java/com/example/simpleblog/controller/PostController.java @@ -1,12 +1,11 @@ package com.example.simpleblog.controller; import com.example.simpleblog.request.PostCreate; +import com.example.simpleblog.request.PostSearch; import com.example.simpleblog.response.PostResponse; import com.example.simpleblog.service.PostService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @@ -30,7 +29,7 @@ public class PostController { } @GetMapping("/posts") - public List getPostList(@PageableDefault Pageable pageable) { - return postService.getPostList(pageable); + public List getPostList(PostSearch postSearch) { + return postService.getPostList(postSearch); } } diff --git a/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepository.java b/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepository.java index b575a87d..62811fa9 100644 --- a/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepository.java +++ b/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepository.java @@ -3,5 +3,5 @@ package com.example.simpleblog.repository; import com.example.simpleblog.domain.Post; import org.springframework.data.jpa.repository.JpaRepository; -public interface PostRepository extends JpaRepository { +public interface PostRepository extends JpaRepository, PostRepositoryCustom { } diff --git a/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepositoryCustom.java b/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepositoryCustom.java new file mode 100644 index 00000000..6663c130 --- /dev/null +++ b/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepositoryCustom.java @@ -0,0 +1,11 @@ +package com.example.simpleblog.repository; + +import com.example.simpleblog.domain.Post; +import com.example.simpleblog.request.PostSearch; + +import java.util.List; + +public interface PostRepositoryCustom { + + List getList(PostSearch postSearch); +} diff --git a/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepositoryImpl.java b/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepositoryImpl.java new file mode 100644 index 00000000..7c9c0771 --- /dev/null +++ b/simpleblog/src/main/java/com/example/simpleblog/repository/PostRepositoryImpl.java @@ -0,0 +1,27 @@ +package com.example.simpleblog.repository; + +import com.example.simpleblog.domain.Post; +import com.example.simpleblog.request.PostSearch; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static com.example.simpleblog.domain.QPost.post; + +@Repository +@RequiredArgsConstructor +public class PostRepositoryImpl implements PostRepositoryCustom { + + private final JPAQueryFactory jpaQueryFactory; + + @Override + public List getList(PostSearch postSearch) { + return jpaQueryFactory.selectFrom(post) + .limit(postSearch.getSize()) + .offset(postSearch.getOffset()) + .orderBy(post.id.desc()) + .fetch(); + } +} diff --git a/simpleblog/src/main/java/com/example/simpleblog/request/PostSearch.java b/simpleblog/src/main/java/com/example/simpleblog/request/PostSearch.java new file mode 100644 index 00000000..b9ea19ce --- /dev/null +++ b/simpleblog/src/main/java/com/example/simpleblog/request/PostSearch.java @@ -0,0 +1,24 @@ +package com.example.simpleblog.request; + +import lombok.Builder; +import lombok.Data; + +import static java.lang.Math.*; + +@Data +@Builder +public class PostSearch { + + private final int MAX_SIZE = 2000; + + @Builder.Default + private Integer page = 1; + + @Builder.Default + private Integer size = 10; + + public long getOffset() { + return (long) (max(1, page) - 1) * min(size, MAX_SIZE); + } + +} diff --git a/simpleblog/src/main/java/com/example/simpleblog/service/PostService.java b/simpleblog/src/main/java/com/example/simpleblog/service/PostService.java index cad04991..3e0e05d2 100644 --- a/simpleblog/src/main/java/com/example/simpleblog/service/PostService.java +++ b/simpleblog/src/main/java/com/example/simpleblog/service/PostService.java @@ -3,10 +3,10 @@ package com.example.simpleblog.service; import com.example.simpleblog.domain.Post; import com.example.simpleblog.repository.PostRepository; import com.example.simpleblog.request.PostCreate; +import com.example.simpleblog.request.PostSearch; import com.example.simpleblog.response.PostResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; @@ -40,9 +40,9 @@ public class PostService { .build(); } - public List getPostList(Pageable pageable) { + public List getPostList(PostSearch postSearch) { // Pageable pageable = PageRequest.of(page, 5, Sort.Direction.DESC, "id"); - return postRepository.findAll(pageable).stream() + return postRepository.getList(postSearch).stream() .map(PostResponse::new) .collect(Collectors.toList()); } diff --git a/simpleblog/src/test/java/com/example/simpleblog/controller/PostControllerTest.java b/simpleblog/src/test/java/com/example/simpleblog/controller/PostControllerTest.java index a9e29b7b..ddebd730 100644 --- a/simpleblog/src/test/java/com/example/simpleblog/controller/PostControllerTest.java +++ b/simpleblog/src/test/java/com/example/simpleblog/controller/PostControllerTest.java @@ -155,11 +155,39 @@ class PostControllerTest { postRepository.saveAll(requestPosts); // expected - mockMvc.perform(get("/posts?page=1&size=5&sort=id,desc") + mockMvc.perform(get("/posts?page=1&size=10") .contentType(APPLICATION_JSON) ) .andExpect(status().isOk()) - .andExpect(jsonPath("$.length()", is(5))) + .andExpect(jsonPath("$.length()", is(10))) + .andExpect(jsonPath("$[0].id").value("30")) + .andExpect(jsonPath("$[0].title").value("제목 30")) + .andExpect(jsonPath("$[0].content").value("내용 30")) + .andDo(print()) + ; + + } + + @Test + @DisplayName("페이지를 0으로 요청하면 첫 페이지를 가져온다.") + void get_post_list_page_0() throws Exception { + // given + List requestPosts = IntStream.range(1, 31) + .mapToObj(i -> Post.builder() + .title("제목 " + i) + .content("내용 " + i) + .build() + ) + .collect(Collectors.toList()); + + postRepository.saveAll(requestPosts); + + // expected + mockMvc.perform(get("/posts?page=0&size=10") + .contentType(APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()", is(10))) .andExpect(jsonPath("$[0].id").value("30")) .andExpect(jsonPath("$[0].title").value("제목 30")) .andExpect(jsonPath("$[0].content").value("내용 30")) diff --git a/simpleblog/src/test/java/com/example/simpleblog/service/PostServiceTest.java b/simpleblog/src/test/java/com/example/simpleblog/service/PostServiceTest.java index 40fe7841..004b6aa5 100644 --- a/simpleblog/src/test/java/com/example/simpleblog/service/PostServiceTest.java +++ b/simpleblog/src/test/java/com/example/simpleblog/service/PostServiceTest.java @@ -3,13 +3,13 @@ package com.example.simpleblog.service; import com.example.simpleblog.domain.Post; import com.example.simpleblog.repository.PostRepository; import com.example.simpleblog.request.PostCreate; +import com.example.simpleblog.request.PostSearch; import com.example.simpleblog.response.PostResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.domain.PageRequest; import java.util.List; import java.util.stream.Collectors; @@ -17,7 +17,6 @@ import java.util.stream.IntStream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.springframework.data.domain.Sort.Direction.DESC; @SpringBootTest class PostServiceTest { @@ -87,11 +86,14 @@ class PostServiceTest { // when List posts = postService.getPostList( - PageRequest.of(0, 5, DESC, "id") + PostSearch.builder() + .page(1) + .size(10) + .build() ); // then - assertEquals(5L, posts.size()); + assertEquals(10L, posts.size()); assertEquals("제목 30", posts.get(0).getTitle()); assertEquals("내용 30", posts.get(0).getContent()); assertEquals("제목 26", posts.get(4).getTitle());