{"id":116,"date":"2020-03-12T23:33:33","date_gmt":"2020-03-12T14:33:33","guid":{"rendered":"http:\/\/www.skyer9.pe.kr\/wordpress\/?p=116"},"modified":"2020-03-21T14:36:45","modified_gmt":"2020-03-21T05:36:45","slug":"from-hello-to-querydsl-add-search-4-12","status":"publish","type":"post","link":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=116","title":{"rendered":"[From Hello To QueryDSL] Add Search (4\/12)"},"content":{"rendered":"<h1>Add Search<\/h1>\n<p>\uac80\uc0c9\uc870\uac74\uc744 \ucd94\uac00\ud569\ub2c8\ub2e4. \uc77c\ub2e8, \uac80\uc0c9\uc870\uac74\uc774 \ud558\ub098\uc77c\ub54c \uc5bc\ub9c8\ub098 \uc27d\uac8c \uac80\uc0c9\uc870\uac74\uc744 \ucd94\uac00\ud560 \uc218 \uc788\ub294\uc9c0 \uc54c\ub824\ub4dc\ub9bd\ub2c8\ub2e4.<\/p>\n<h2>\uac1c\ubc1c\ud658\uacbd<\/h2>\n<ul>\n<li>Spring Boot 2.1.x<\/li>\n<li>Gradle 4.10.2<\/li>\n<\/ul>\n<p>\ud504\ub85c\uc81d\ud2b8\ub294 \uc774\uc804 \uae00\uc5d0\uc11c \uc791\uc131\ub41c \ud504\ub85c\uc81d\ud2b8\uc5d0 \ud30c\uc77c\uc744 \ucd94\uac00 \ub610\ub294 \uc218\uc815\ud558\ub294 \ubc29\uc2dd\uc73c\ub85c \uc9c4\ud589\ub429\ub2c8\ub2e4. \uc774\uc804 \uae00\uc744 \ub530\ub77c \ud558\uc9c0 \uc54a\uc740 \uacbd\uc6b0, \uba3c\uc800 \uc774\uc804 \uae00\ub300\ub85c \ud504\ub85c\uc81d\ud2b8\ub97c \uad6c\uc131\ud558\uc2dc\uae30 \ubc14\ub78d\ub2c8\ub2e4.<\/p>\n<h2>\ud30c\uc77c\ucd94\uac00<\/h2>\n<p>\uc5c6\uc74c<\/p>\n<h2>\ud30c\uc77c\uc218\uc815<\/h2>\n<p>\uc544\ub798 <code>findByTitle()<\/code> \uba54\uc18c\ub4dc \ud558\ub098 \ucd94\uac00\ud558\ub294 \uac83\uc73c\ub85c \ucffc\ub9ac \uc0dd\uc131\uc774 \ub05d\ub0a9\ub2c8\ub2e4.<\/p>\n<p>findByTitle \uc774\ub77c\ub294 \uba54\uc18c\ub4dc\uba85\uc73c\ub85c \ubcc0\uc218\uba85\uc774 title \uc778 \ubcc0\uc218\ub97c \ucc3e\uc544 where \uc808\uc5d0 \ucd94\uac00\ud558\ub294 \ubc29\uc2dd\uc785\ub2c8\ub2e4.<\/p>\n<p>src\/main\/java\/kr\/co\/episode\/example\/domain\/posts\/PostsPagingRepository.java<\/p>\n<pre><code class=\"language-java\">\/\/ ......\npublic interface PostsPagingRepository extends JpaRepository&lt;Posts, Long&gt; {\n\n    @Query(value = &quot;SELECT p FROM Posts p ORDER BY p.id DESC&quot;)\n    Page&lt;Posts&gt; findAllDesc(Pageable pageable);\n\n    Page&lt;Posts&gt; findByTitle(Pageable pageable, String title);\n}\n\/\/ ......<\/code><\/pre>\n<p><code>Pageable<\/code> \uc758 \uae30\ubcf8\uc801\uc778 \uc815\ub82c\uc21c\uc11c\ub294 <code>id<\/code> \uc21c\ucc28\uc815\ub82c\uc774\ub77c, \uc815\ub82c\uc21c\uc11c\ub97c \ubcc0\uacbd\ud558\ub294 \ucf54\ub4dc\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4.<\/p>\n<p>src\/main\/java\/kr\/co\/episode\/example\/service\/posts\/PostsPagingService.java<\/p>\n<pre><code class=\"language-java\">\/\/ ......\n@RequiredArgsConstructor\n@Service\npublic class PostsPagingService {\n\n    private final PostsPagingRepository postsPagingRepository;\n\n    \/\/ ......\n\n    @Transactional(readOnly = true)\n    public Page&lt;PostsListResponseDto&gt; findAllDesc(Pageable pageable, String title) {\n        Page&lt;Posts&gt; posts;\n        if (&quot;&quot;.equals(title)) {\n            posts = postsPagingRepository.findAllDesc(pageable);\n        } else {\n            \/\/ \uc815\ub82c\uc21c\uc11c \ubcc0\uacbd\n            Pageable p = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(&quot;id&quot;).descending());\n            posts = postsPagingRepository.findByTitle(p, title);\n        }\n        List&lt;PostsListResponseDto&gt; results = posts.getContent().stream()\n                .map(PostsListResponseDto::new)\n                .collect(Collectors.toList());\n        long totalCount = posts.getTotalElements();\n\n        return new PageImpl&lt;&gt;(results, pageable, totalCount);\n    }\n}\n\/\/ ......<\/code><\/pre>\n<p>src\/main\/java\/kr\/co\/episode\/example\/web\/IndexController.java<\/p>\n<pre><code class=\"language-java\">@RequiredArgsConstructor\n@Controller\npublic class IndexController {\n\n    private final PostsService postsService;\n    private final PostsPagingService postsPagingService;\n\n    @GetMapping(&quot;\/&quot;)\n    public String index(Model model, @PageableDefault Pageable pageable,\n                        @RequestParam(value = &quot;title&quot;, required = false, defaultValue = &quot;&quot;) String title) {\n        \/\/model.addAttribute(&quot;posts&quot;, postsService.findAllDesc());\n\n        pageable = PageRequest.of(pageable.getPageNumber(), 3);\n        \/\/model.addAttribute(&quot;posts&quot;, postsPagingService.findAllDesc(pageable));\n        model.addAttribute(&quot;posts&quot;, postsPagingService.findAllDesc(pageable, title));\n        model.addAttribute(&quot;title&quot;, title);\n\n        return &quot;index&quot;;\n    }\n\n    \/\/ ......\n}<\/code><\/pre>\n<p>src\/main\/resources\/templates\/index.html<\/p>\n<pre><code class=\"language-java\">&lt;!DOCTYPE html&gt;\n&lt;html xmlns:th=&quot;http:\/\/www.thymeleaf.org&quot;&gt;\n&lt;head&gt;\n    &lt;meta charset=&quot;UTF-8&quot;&gt;\n    &lt;title&gt;\uac8c\uc2dc\ud310&lt;\/title&gt;\n    &lt;link rel=&quot;stylesheet&quot; href=&quot;https:\/\/maxcdn.bootstrapcdn.com\/bootstrap\/3.3.2\/css\/bootstrap.min.css&quot;&gt;\n    &lt;style&gt;\n        .container p { display: inline }\n    &lt;\/style&gt;\n&lt;\/head&gt;\n&lt;body class=&quot;container&quot;&gt;\n\n&lt;h1&gt;\uc2a4\ud504\ub9c1 \ubd80\ud2b8 \uac8c\uc2dc\ud310&lt;\/h1&gt;\n\n&lt;div class=&quot;col-md-12&quot;&gt;\n    &lt;div class=&quot;row&quot;&gt;\n        &lt;div class=&quot;col-md-6&quot;&gt;\n            &lt;a href=&quot;\/posts\/save&quot; role=&quot;button&quot; class=&quot;btn&quot; btn-primary&gt;\uae00 \ub4f1\ub85d&lt;\/a&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;div style=&quot;height: 80px;&quot;&gt;\n\n&lt;\/div&gt;\n\n&lt;table class=&quot;table&quot;&gt;\n    &lt;tr&gt;\n        &lt;th&gt;\uae00 \ubc88\ud638&lt;\/th&gt;\n        &lt;th&gt;\uae00\uc4f4\uc774&lt;\/th&gt;\n        &lt;th&gt;\uae00 \uc81c\ubaa9&lt;\/th&gt;\n        &lt;th&gt;\ucd5c\uc885\uc218\uc815&lt;\/th&gt;\n    &lt;\/tr&gt;\n    &lt;tr th:each=&quot;posts: ${posts}&quot;&gt;\n        &lt;td th:text=&quot;${posts.id}&quot;&gt;&lt;\/td&gt;\n        &lt;td&gt;\n            &lt;a th:href=&quot;${posts.link}&quot; th:text=&quot;${posts.author}&quot; \/&gt;\n        &lt;\/td&gt;\n        &lt;td&gt;\n            &lt;a th:href=&quot;${posts.link}&quot; th:text=&quot;${posts.title}&quot; \/&gt;\n        &lt;\/td&gt;\n        &lt;td th:text=&quot;${posts.modifiedDate}&quot;&gt;&lt;\/td&gt;\n    &lt;\/tr&gt;\n&lt;\/table&gt;\n\n&lt;div class=&quot;col-md-12&quot;&gt;\n    &lt;div class=&quot;col-md-4&quot;&gt;\n        &lt;form name=&quot;frm&quot;&gt;\n            &lt;input type=&quot;hidden&quot; name=&quot;page&quot; value=&quot;0&quot;&gt;\n            &lt;div class=&quot;form-group&quot;&gt;\n                &lt;label for=&quot;title&quot;&gt;\uc81c\ubaa9&lt;\/label&gt;\n                &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;title&quot; placeholder=&quot;\uc81c\ubaa9\uc744 \uc785\ub825\ud558\uc138\uc694.&quot; name=&quot;title&quot; th:value=&quot;${title}&quot; \/&gt;\n            &lt;\/div&gt;\n        &lt;\/form&gt;\n\n        &lt;button type=&quot;button&quot; class=&quot;btn btn-primary&quot; id=&quot;btn-search&quot;&gt;\uac80\uc0c9&lt;\/button&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;nav style=&quot;text-align: center;&quot;&gt;\n    &lt;ul class=&quot;pagination&quot;\n        th:with=&quot;start=${T(Math).floor(posts.number\/10)*10},\n                    last=(${start + 9 &lt; posts.totalPages-1 ? start + 9 : posts.totalPages-1})&quot;&gt;\n        &lt;li&gt;\n            &lt;a th:onclick=&quot;|javascript:gotoPage(0)|&quot; aria-label=&quot;First&quot;&gt;\n                &lt;span aria-hidden=&quot;true&quot;&gt;First&lt;\/span&gt;\n            &lt;\/a&gt;\n        &lt;\/li&gt;\n\n        &lt;li th:class=&quot;${posts.first} ? &#039;disabled&#039;&quot;&gt;\n            &lt;a th:onclick=&quot;|javascript:gotoPage(${posts.number-1})|&quot; aria-label=&quot;Previous&quot;&gt;\n                &lt;span aria-hidden=&quot;true&quot;&gt;&lt;&lt;\/span&gt;\n            &lt;\/a&gt;\n        &lt;\/li&gt;\n\n        &lt;li th:each=&quot;page: ${#numbers.sequence(start, last)}&quot; th:class=&quot;${page == posts.number} ? &#039;active&#039;&quot;&gt;\n            &lt;a th:text=&quot;${page+1}&quot; th:onclick=&quot;|javascript:gotoPage(${page})|&quot; &gt;&lt;\/a&gt;\n        &lt;\/li&gt;\n\n        &lt;li th:class=&quot;${posts.last} ? &#039;disabled&#039;&quot;&gt;\n            &lt;a th:onclick=&quot;|javascript:gotoPage(${posts.number+1})|&quot; aria-label=&quot;Next&quot;&gt;\n                &lt;span aria-hidden=&quot;true&quot;&gt;&gt;&lt;\/span&gt;\n            &lt;\/a&gt;\n        &lt;\/li&gt;\n\n        &lt;li&gt;\n            &lt;a th:onclick=&quot;|javascript:gotoPage(${posts.totalPages-1})|&quot; aria-label=&quot;Last&quot;&gt;\n                &lt;span aria-hidden=&quot;true&quot;&gt;Last&lt;\/span&gt;\n            &lt;\/a&gt;\n        &lt;\/li&gt;\n    &lt;\/ul&gt;\n&lt;\/nav&gt;\n\n&lt;script src=&quot;https:\/\/code.jquery.com\/jquery-3.3.1.min.js&quot;&gt;&lt;\/script&gt;\n&lt;script src=&quot;https:\/\/stackpath.bootstrapcdn.com\/bootstrap\/4.3.1\/js\/bootstrap.min.js&quot;&gt;&lt;\/script&gt;\n\n&lt;script src=&quot;\/js\/app\/index.js&quot;&gt;&lt;\/script&gt;\n&lt;script&gt;\n    function gotoPage(pg) {\n        document.frm.page.value = pg;\n        document.frm.submit();\n    }\n&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\uac80\uc0c9\uc870\uac74\uc744 \ucd94\uac00\ud569\ub2c8\ub2e4. \uc77c\ub2e8, \uac80\uc0c9\uc870\uac74\uc774 \ud558\ub098\uc77c\ub54c \uc5bc\ub9c8\ub098 \uc27d\uac8c \uac80\uc0c9\uc870\uac74\uc744 \ucd94\uac00\ud560 \uc218 \uc788\ub294\uc9c0 \uc54c\ub824\ub4dc\ub9bd\ub2c8\ub2e4.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-116","post","type-post","status-publish","format-standard","hentry","category-spring-boot-2-1"],"_links":{"self":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/116","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=116"}],"version-history":[{"count":6,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/116\/revisions"}],"predecessor-version":[{"id":222,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/116\/revisions\/222"}],"wp:attachment":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}