JavaでTODOアプリを制作しよう6 検索機能の実装


こんにちは。今回はSpringで検索機能の実装を行いたいと思います。

TODOアプリ作成リンク集

1: [超基礎の理解] MVCの簡単な説明
2: [雛形を用意する] Spring Initializrで雛形を作ってHello worldしたい
3: [MySQLとの接続・設定・データの表示] MySQLに仮のデータを保存 -> 全取得 -> topに表示する
4: [POST機能] 投稿機能の実装
5: [PATCH機能] TODOの表示を切り替える
6: [JpaRepositoryの簡単な使い方] 検索機能の実装(今ここ)

実装の流れ

search.htmlで検索ワードが入力される

コントローラで検索ワードを受け取りサービスクラスへ送る

サービスクラスではレポジトリに検索処理を依頼する

レポジトリが結果をサービスに返す

サービスがコントローラに結果を返す

コントローラがsearch.htmlに結果を返す

html上に表示される

......といった感じです!

レポジトリクラスの編集

@Repository
public interface TodoRepository extends JpaRepository<TodoEntity, Long> {

    //↓追加する
    List<TodoEntity> findByTitleContaining(String searchWord);

}

Jpaには検索に便利なメソッドが複数用意されていてfindByTitleContainingこうする事でtitleというカラムに引数内の文言が含まれているかを検索してくれます。

サービスクラスの編集

   public List<TodoEntity> findToDoByTitle(String searchWord) {
        return todoRepository.findByTitleContaining(searchWord);
    }

この関数を追加してください。

文字列である引数searchWordを先ほどのレポジトリの関数に送っています。戻り値はListですね。

コントローラの編集

    @GetMapping("/search")
    public String showSearch() {
        return "search";
    }

    @GetMapping("/search/result")
    public String searchResult(Model model, @ModelAttribute TodoSearchForm searchForm) {
        List<TodoEntity> searchResult = todoService.findToDoByTitle(searchForm.getSearchWord());
        model.addAttribute("searchResults", searchResult);
        return "search";
    }

この二つを追加します。

上の/searchは検索画面を表示するもので、下の/serach/resultが検索結果を表示するためにあります。

model.addAtrribute()を使う事でhtmlファイルに取得したデータを送る事ができます。今回はList型のsearchResultを送っています。

TodoSearchFormクラスの作成

検索時に入力された文字を取り扱う器のクラスを作ります。

TodoSearchForm
package com.example.todo;

import lombok.Data;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Data
public class TodoSearchForm {
    private Long id;
    @NotNull
    @Size(min = 0, max = 30)
    private String searchWord;
}

@DataはLombokのアノテーションです。ゲッタ、セッタなどを省略してくれます。
今回重要なのはString searchWordを格納できる様にしている点ですね。

search.htmlの作成

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>hello</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head><body>
<!-- Header -->
<div class="container mt-5">
    <form th:action="@{/search/result}" method="GET">
        <div class="row">
            <input type="text" name="searchWord" class="col-8">
            <button type="submit" class="ml-3 col-3 btn-primary w-25 btn-lg">検索</button>
        </div>
    </form>
</div>

<div th:if="!${#lists.isEmpty(searchResults)}" class="container mt-5">
    <p>検索結果が<span  th:text="${#lists.size(searchResults)}"></span>件あります</p>
</div>

<div th:each="todo: ${searchResults}" class=" w-75 h-25 my-1 mx-auto pt-5">
    <div class="container">
        <div  class="row">
            <div class="col-5 pl-5">
                <p th:text="${todo.title}" class="mb-1"></p>
                <p class="mb-1">期限:<span  th:text="${todo.deadline}"></span></p>
                <p class="mb-1">作成日時:<span  th:text="${todo.createTime}"></span></p>
            </div>
            <div class="col-2 d-flex justify-content-start align-items-center px-0">
                <a th:href="@{/edit/{id}(id=${todo.id})}" class="h-100 w-75 btn btn-info pt-4">
                    編集
                </a>
            </div>
            <div th:if="${todo.status}" class="col-2 d-flex px-0">
                <div class="h-100 w-75 badge bg-success text-white d-flex align-items-center">
                    <h3 class=" my-1 mx-auto">完了</h3>
                </div>
            </div>
            <div th:unless="${todo.status}" class="col-2 d-flex px-0">
                <div class="h-100 w-75 badge bg-danger text-white d-flex align-items-center">
                    <h3 class=" my-1 mx-auto">未完了</h3>
                </div>
            </div>
        </div>
    </div>
</div>
<!--    bootstrap js読み込み-->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
</body>
</html>

ポイントですが下記の様にすることで検索結果が何件あるかを表すことができます!

<div th:if="!${#lists.isEmpty(searchResults)}" class="container mt-5">
    <p>検索結果が<span  th:text="${#lists.size(searchResults)}"></span>件あります</p>
</div>

まとめ

どうだったでしょうか?

今回の重要な点はJpaレポジトリの部分だと思います。

ここにリファレンスがあるのでチェックしてみると良いかと思います!