3
0

一覧機能を作成していきましょう。

2.設計で検討した一覧機能をおさらい

機能一覧
2.タスクの一覧表示:
ユーザーがToDoリスト内の全てのタスクを閲覧できる機能。タスクのタイトル、説明、期日、ステータスを表示。
 ステータスは、未着手、作業中、完了

画面遷移
一覧画面 ⇔ 登録画面 ⇔ 確認画面 → 完了画面 (→ 一覧画面)
     ⇔ 変更画面 ⇔ 確認画面 → 完了画面 (→ 一覧画面)
     →削除確認画面 → 完了画面(→ 一覧画面)

画面一覧

  • 一覧画面

画面項目

  • 一覧画面
    新規登録 ボタン 新規登録画面に遷移する
    タイトル 出力 繰り返しあり
    説明 出力 繰り返しあり
    期限 出力 繰り返しあり yyyy/mm/dd
    変更 ボタン 変更画面に遷移・繰り返しあり

ここまでみていくと、一覧画面が一覧機能をつかさどっていることがわかります。登録済のタスクが一覧で並んでいて、登録画面や変更画面、削除確認画面へも遷移することができます。また、ユーザーが最初に遷移する画面にもなっていますので、今回のTODOアプリケーションの肝になる画面のようです。

以下のような画面になります。
image.png

ユーザーからの見られ方を想像してみます。

  1. ブラウザから一覧画面にアクセス
  2. 一覧画面が表示される

処理としては、一覧画面を初期表示させるときに、DBにタスクを取得し、一覧画面を表示します。URL設計の以下の部分が該当します。

URL: /task/list
HTTPメソッド: GET
処理: タスク一覧表示

実装方針(箇条書き)

  • Controllerクラス

    • HTTPリクエストを受け取り、適切なServiceクラスのメソッドを呼び出す。
    • 必要なデータを取得し、ビューに渡して適切なHTMLファイルを返す。
  • Serviceクラス

    • ビジネスロジックを実装。
    • データの操作や処理を行い、Repositoryクラスを介してデータベースとのやり取りを行う。
    • ビジネスロジックをControllerクラスから切り離し、コードの再利用性や保守性を向上。
  • Repositoryクラス

    • データベースとのやり取りを担当。
    • Mapperクラスを介してSQLクエリを定義し、Repositoryクラスから呼び出す。
  • Mapperクラス

    • MyBatisを使用してSQLクエリを実行し、データの永続化や取得、更新、削除などの操作を行う。

実装の流れ

  • まずControllerクラスから始めて、リクエストに対する処理を実装。
  • 次にServiceクラスでビジネスロジックを実装し、必要なデータの操作を行う。
  • Repositoryクラスを実装し、Mapperクラスを呼び出す。
  • Mapperクラスを実装し、MyBatisを使用してSQLクエリを実行するメソッドを定義。
  • 最後にHTMLファイルを作成し、ThymeleafでHTMLにマージする。

今回は、一覧画面で表示するタスクの一覧を取得する処理を書いていきます。それでは、Javaの実装を行いましょう。

controllerクラスを書いてみよう

まず、パッケージをつくります
src/main/javaの上で右クリック→新規→パッケージ
「com.example.demo.controller」

では、作ったcontrollerパッケージの中にクラスを書いていきます。
名前をTaskController.javaとします。クラス名は最初の文字を大文字にします。

以下のようなコードが表示されます。

package com.example.demo.controller;

public class TaskController {

}

まず、クラスに「@Controller」をつけましょう。このクラスがControllerクラスであることをフレームワーク側に認知してもらいます。

@Controller
public class TaskController {

}

次に、HTTPリクエストを受け取り、ビューに渡して適切なHTMLファイルを返すようにメソッドを書きます。URL設計で検討したものを参考に、HTTPリクエストは、GETメソッドで「/task/list」というURLで受け取ります。返り値に記載するHTMLファイルは、taskフォルダのindexという名前にしましょう。

	@GetMapping(value = "/task/list")
	public String showTask(Model model) {
		
		return "task/index";
	}

HTTPリクエストは、GETメソッドで「/task/list」というURLで受け取ります。

上記の表現は、「@GetMapping(value = "/task/list")」で表現しています。
@GetMappingはインポート文が必要になるので、エクリプスの補助機能などで、インポートしてください。

returnでは、src/main/resouces>templates配下のファイルが呼び出されます。このあたりの紐づけはフレームワーク側で行ってくれます。逆にいうと、templates配下のフォルダ構成とreturnの指定に間違えがあると、適切にhtmlを呼び出せません。ので気をつけましょう。ちなみに、「.html」という拡張子は省略できます。

HTMLファイルのフォルダ構成は以下のようになりますね。Javaでcontroller~mapperクラスまで書いたら、htmlファイルを書くので、その時index.htmlの中身を書いていきましょう。

src/main/resouces
 |-templates
   |-task/index.html

Serviceクラスのメソッドを呼び出す記載をしましょう。

controllerオブジェクトはserviceオブジェクトを呼び出すことが必要です。そのため、controllerクラスにserviceオブジェクトが必要です。
コンストラクタインジェクションという方法を使って、Serviceオブジェクトをインジェクションします。書き方は以下の通りです。

    private final TaskService taskService;

    public TaskController(TaskService taskService) {
        this.taskService = taskService;
    }

Serviceオブジェクトを格納するフィールド(taskService)を記載します。次に、TaskServiceを引数とするコンストラクタ(TaskController)で、引数をフィールドにセットします。このような記載をすることで、controllerクラスでServiceオブジェクトを呼び出すことができます。まだ、serviceクラスを作成していなので、(エクリプス側の機能によって)赤い波線が惹かれますが、気にしないでOKです。

【FYI】
【Spring】@Autowiredはコンストラクタインジェクションで使おう!
https://qiita.com/yuto-hatano/items/69d01343f710117e4243

serviceクラスの一覧取得処理を呼び出し、一覧データを受け取りmodelにセットして画面に渡すコードを書きます。
画面にデータを渡すには、Modelオブジェクトにデータを格納します。

	@RequestMapping(value = "/task/list", method = RequestMethod.GET)
	public String showTask(Model model) {
		
		//タスクの一覧を取得
		List<Task> taskList = taskService.findAll();
				
		model.addAttribute("taskList", taskList);
		return "task/index";
	}

エンティティクラスやserviceクラスを書いていないため、エディタ上でエラーが表示されるかもしれませんが。。。
あとは、controllerクラスで必要なインポート文が記載されていればOKです。インポート文がエディタのサポート機能で追加してくれるはずです。

serviceクラスを書いてみよう

controllerクラスを書いた時と同じように、まず、パッケージをつくります
src/main/javaの上で右クリック→新規→パッケージ
「com.example.demo.service」

次に、作ったserviceパッケージの中にインターフェースとクラスを書いていきます。インターフェースをTaskService.java、実装クラスをTaskServiceImpl.javaとします。以下のようなコードが表示されます。

package com.example.demo.service;

public interface TaskService {

}
package com.example.demo.service;

public class TaskServiceImpl {

}

serviceクラスはインターフェースを用いてコードを書いていきます。インターフェースを用いる主なメリットは、実装を追加したり変更し足りする際に、既存のコードへの影響を少なくして新しい実装に差し替えることができることです。

インターフェースから記載していきます。今回はcontrollerクラスでserviceクラスのメソッド名をすでに書いていますので、それに沿って記載します。

List<Task> findAll();

実装クラスを記載します。インターフェースを実装するためをimplementsを書きます。クラスには@Serviceというアノテーションもつけましょう

repositoryクラスのメソッドを呼び出すため、serviceクラスには、repositoryクラスのオブジェクトが必要です。controllerクラスでserviceクラスを呼び出す際は、コンストラクタインジェクションという方法を使いましたが、練習のためもう一つの方法であるフィールドインジェクションで記載しましょう。

	@Autowired
	TaskRepository taskRepository;

タスクの一覧を取得するだけで、ビジネスロジックとしてserviceクラスで何か処理を追記するものはないので、repositoryクラスのメソッドを呼び出します。

public class TaskServiceImpl implements TaskService{

	@Override
	public List<Task> findAll() {
		return taskRepository.findAll();
	
}

【FYI】
【詳解】抽象クラスとインタフェースを使いこなそう!!
https://qiita.com/yoshinori_hisakawa/items/cc094bef1caa011cb739

repositoryクラスを書いてみよう

controllerクラス、serviceクラスを書いた時と同じように、まず、パッケージをつくります
src/main/javaの上で右クリック→新規→パッケージ
「com.example.demo.repository」

では、作ったrepositoryパッケージの中にクラスを書いていきます。
名前をTaskRepository.javaとします。

以下のようなコードが表示されます。

package com.example.demo.repository;

public class TaskRepository {

}

一見必要のないようなクラスに見えますが、どうしてRepositoryクラスを作成するのでしょうか?その理由は主に3つあります。1つ目は、役割の明確化です。Repositoryクラスは、データベースとの直接のやり取りを担当しServiceクラスはビジネスロジックを実装します。このように役割を明確に分離することで、コードの可読性と保守性が向上します。2つ目は、再利用性の向上です。同じデータベース操作が複数のServiceクラスから再利用でき、コードの重複を避けることができます。3つ目は、テストの容易化です。テスト時にデータベース操作などモックを作成しやすくなります。

Mapperクラスのメソッドを呼び出すため、Repositoryクラスには、mapperクラスのオブジェクトが必要です。ということは、インジェクションが必要ですね。そして、Repositoryクラスは、serviceクラスとmapperクラスを仲介するクラスなので、以下のように書きます。@Repositoryというアノテーションもクラスに忘れずにつけましょう。

    private final TaskMapper taskMapper;
    public TaskRepository(TaskMapper taskMapper) {
        this.taskMapper = taskMapper;
    }

    public List<Task> findAll() {
        return taskMapper.findAll();
    }

エンティティクラスを書いてみよう

次にエンティティクラスを書いていきます。

com.example.demo.entityという名前でパッケージを作成し、Task.javaというクラスを作成します。以下のようなコードが表示されます。

package com.example.demo.entity;

public class Task {

}

エンティティクラスは、テーブルを作成したときのカラム名を参考にキャメルケースで書いていきましょう。
今書いているのは、タスクの一覧を表示する処理なので、この処理で使う項目だけ記載できていればOKです。が、最終的にはtaskテーブルの項目をこのクラスに書いていくため、このタイミングで一旦teskテーブルの項目を書いておきます。(後から書き足すということを忘れそうなので。)

    // タスクID
    private int taskId;

    // タイトル
    private String title;

    // 説明
    private String description;

    // 期限
    private LocalDateTime deadline;

    // ステータス
    private int status;

    // ユーザーID
    private int userId;

    // 削除フラグ
    private boolean deleteFlg;

    // 更新日時
    private LocalDateTime updateTime;

    // 作成日時
    private LocalDateTime createTime;

ゲッターセッターも書いていくのですが、エクリプスの機能で自動生成できます。

【FYI】
【Eclipse】getter/setterを一瞬で記述する方法【Java】
https://begi-tech.com/article/java/eclipse-getter-setter-java

mybatisを書いてみよう

次に、mapperクラスとxmlでSQLを書いていきます。

ます、mapperのインターフェースを書いていきます。
/src/main/java配下に、パッケージとインターフェースを作りましょう。
「com.example.demo.mapper」、「TaskMapper」という名前とします。

以下のようなコードが表示されます。

package com.example.demo.mapper;

public interface TaskMapper {

}

@Mapperのアノテーションをインターフェースにつけて、インターフェースの中身は以下のように書きます

List<Task> findAll();

次に、xmlファイルを書いていきます。
今までは、/src/main/java配下にクラスを書いていましたが、xmlファイルは
src/main/resource com/example/demo/mapper 配下に作成します。ここでポイントなのは、com以下の構成をTaskMapper.javaとファイル構造と合わせることです。ファイル構造が一致していれば、自動でインターフェースとxmlファイルがマッピングされます。

ということで、src/main/resource com/example/demo/mapper 配下に、TaskMapper.xmlファイルを作成します。

まず、このファイルがmapperであることを宣言する「おまじない」を書きます。namespaceには、TaskMapperまでのパスを書きます。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.TaskMapper">
	
    
</mapper>

mapperのタグの中にSQLを書いていきます。タスクテーブルからタスクの全量を取得するSQLをselectタグの中に書いていきます。selectのSQLを使う際は、タグを利用してSQLを書きます。id属性はSQLの識別名です。名前をつけて識別できるようにします。resultType属性で検索結果をどこにマッピングするか指定します。今回はTaskentityに検索結果を格納します。

	<select id="findAll" resultType="com.example.demo.entity.Task">
        SELECT * FROM task where deleteFlg = 0;
    </select>

resultTypeにはselectで取得した値の格納先を指定するのですが、このパスやファイル名が間違っていてもエクリプス側で指摘されません。そのため、スペルミスを探すのが一苦労です。SQLでスペルミスがあっても、同様にエクリプスで指摘されません。workbenchで動くことを確認してから、xmlに書くなどスペルミスのなくす工夫ができるとよいかもしれません。

htmlマージをしてみよう

本投稿ではJavaを記載することに注力したいので、htmlについてはあまり深く扱いません。ここでは、主にThymeleafを書くところに注力していきます。設計で決めた仕様と画面イメージをもとにbooststrapでhtmlを作成しました。

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>List page</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
  </head>
  <body>

    <nav class="navbar bg-body-tertiary">
        <div class="container-fluid">
          <span class="navbar-brand mb-0 h1">Practice</span>
        </div>
    </nav>

    <div class="container">
        <div class="px-4 pt-3 my-3 text-center border-bottom">
            <h1 class="display-4 fw-bold text-body-emphasis mb-3">ToDo Application</h1>
            <div class="col-lg-6 mx-auto">
              <div class="d-grid gap-2 d-sm-flex justify-content-sm-center mb-5">
                <a type="button" class="btn btn-primary btn-lg px-4 me-sm-3" href="/task/add">create</a>
              </div>
            </div>
        </div>

        <div class="px-4 pt-2 my-2 text-center">
            <table class="table table-striped">
                <thead>
                  <tr>
                    <th scope="col">#</th>
                    <th scope="col">Title</th>
                    <th scope="col">description</th>
                    <th scope="col">deadline</th>
                    <th scope="col">status</th>
                    <th scope="col">button</th>
                    <th scope="col">button</th>
                  </tr>
                </thead>
                <tbody>
                  <!-- Sample Data -->
                  <tr>
                    <th scope="row">1</th>
                    <td>Title</td>
                    <td>description</td>
                    <td>2023/05/30 15:00</td>
                    <td>未着手</td>
                    <td><a class="btn btn-primary" href="/task/edit/1">edit</a></td>
                    <td><a class="btn btn-primary" href="/task/delete/1">delete</a></td>
                  </tr>
                </tbody>
              </table>
        </div>
      </div>

      <footer class="py-3 my-4">
        <p class="text-center text-body-secondary">&copy; 2023 Company, Inc</p>
      </footer>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
  </body>
</html>

上記のhtmlをまず、template/task配下に、index.htmlという名前で作成します。ファイルの置き場所は、controllerクラスのshowTaskメソッドの返り値と一致させる必要があるのでしたね。

では、このhtmlにtymeleafを組み込んでバックエンド側で取得したタスク一覧を画面で表示できるようにしましょう。
Thymeleafを組み込んでいくのは主に、タスク一覧を表示させる部分です。controllerクラスではDBから取得したタスク一覧を「taskList」という名前で画面に渡そうとしています。一覧の表示部分についてThymeleafを組み込むには、以下のように書きます。

                  <tr th:each="taskList : ${taskList}" th:object="${taskList}">
                    <th scope="row" th:text="*{taskId}">1</th>
                    <td th:text="*{title}">Title</td>
                    <td th:text="*{description}">description</td>
                    <td th:text="${#temporals.format(taskList.deadline, 'yyyy/MM/dd HH:mm')}">Formatted Deadline</td>
                    <td th:text="*{status == 1 ? '未着手' : status == 2 ? '作業中' : '完了'}">Status</td>
                    <td><a class="btn btn-primary" href="/task/edit/1">edit</a></td>
                    <td><a class="btn btn-primary" href="/task/delete/1">delete</a></td>
                  </tr>

Listを画面で表示するには、th:eachを使います。th:eachは、th:eachを書いたHTMLタグ(以下の書き方ではtrタグ)をListの数だけ繰り返し表示します。th:eachの行ではtaskListが2つ出ていてややこしいのですが、${}で括ってあるtaskListがmodelクラスのデータを指定するもの(controllerクラスでmodelクラスにセットし、DBから取得したタスク一覧のデータ)です。th:eachの行で最初に出てくるtaskListがtr内でタスク一覧を扱う変数名です。th:objectとは、オブジェクト全体をバインドするための属性です。フォーム送信時にオブジェクト全体をサーバーに送ることができます。

th:objectを使っているときには「*{}」という書き方をすることができます。そのオブジェクトのプロパティを直接参照することができます。

<!--元々の記述-->
<td th:text="${task.title}">Title</td>
<!--*{}を使った記述-->
<td th:text="*{title}">Title</td>

deadlineについては、日付フォーマットを変形し指定のフォーマット(yyyy/MM/dd HH:mm')で表示するように記載しています。${#temporals.format(...)}はThymeleafの日時操作機能を使っています。

statusについては、三項演算子を重ねる形で記載して、DBではstatusを数字で保持していますが、画面では日本語で表示するようにしています。
「status == 1 ? '未着手': 」ステータスが1なら未着手を表示。
「: status == 2 ? '作業中':」 それ以外でステータスが2なら作業中を表示。
「: '完了':」 その他の場合は完了を表示しています。

【FYI】
Thymeleafチートシート
https://qiita.com/NagaokaKenichi/items/c6d1b76090ef5ef39482

動作確認

ここまででタスク全量をDBから取得し、画面に表示させるコードを書きました。実際に動作するかを確認していきましょう。
まず、DBに値がない状態で、一覧画面が表示されるか確認しましょう。期待値は、「/localhost:8080/taskでアクセスして、(タスクそのものは表示されないが)一覧画面が崩れなく表示されること」です

「プロジェクトエクスプローラー」のプロジェクト名で右クリックして、「実行」→「Spring boot アプリケーション」を押下します。

image.png

ブラウザの検索バーに「/localhost:8080/task/list」と入力します。/task/listはcontrollerクラスのshowTaskメソッドに書いたパスと一致している必要があります。

次に、テーブルにデータを入れてタスク一覧を取得できるかを確認します。一覧画面に表示するダミーデータを用意しました。
workbenchで以下のinsert文でダミーデータをtaskテーブルに挿入し、select文でtaskテーブルに挿入できたか確認します。

//ダミーデータを挿入
INSERT INTO `tutorialtodoapplication`.`task` (`title`, `description`, `deadline`, `status`, `userId`, `deleteFlg`) VALUES
('Task 1', 'Description for Task 1', '2024-06-01 12:00:00', 1, 1, 0),
('Task 2', 'Description for Task 2', '2024-06-02 13:00:00', 2, 2, 0),
('Task 3', 'Description for Task 3', '2024-06-03 14:00:00', 3, 1, 0),
('Task 4', 'Description for Task 4', '2024-06-04 15:00:00', 1, 3, 0),
('Task 5', 'Description for Task 5', '2024-06-05 16:00:00', 2, 2, 0);

//ダミーデータを挿入できたことを確認
select * from tutorialtodoapplication.task;

taskテーブルにダミーデータを挿入できたら、先ほどと同じように動作確認をしましょう。
実行を押下し、ブラウザの検索バーに「/localhost:8080/task/list」と入力します。

ダミーデータが一覧画面に表示されればOKです。

次の投稿では、登録機能を実装していきます。

【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-①イントロダクション-
【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-②設計-
【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-③実装方針と環境構築-
【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-④一覧機能の作成-
【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-⑤新規登録機能の作成-
【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-⑥変更機能の作成-
【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-⑦削除機能の実装-
【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-⑧戻る機能の実装-
【Java】Spring Bootを使ったToDoアプリケーションを作成しよう-⑨例外処理の実装-

ここまでで書いたコードの全量

コメントも入れています。

controllerクラス

package com.example.demo.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.example.demo.entity.Task;
import com.example.demo.service.TaskService;


/**
 * Webアプリケーションのタスク関連機能を担当するControllerクラスです。
 * タスクの一覧表示、登録、変更などの機能が含まれています。
 *
 */
@Controller
public class TaskController {

    private final TaskService taskService;

    public TaskController(TaskService taskService) {
        this.taskService = taskService;
    }

    /**
     * タスクの一覧を表示するメソッドです。
     * 
     * @param model タスク一覧をViewに渡すためのSpringのModelオブジェクト
     * @return "task/index" - タスク一覧表示用のHTMLテンプレートのパス
     */
	@RequestMapping(value = "/task/list", method = RequestMethod.GET)
	public String showTask(Model model) {
		
		//タスクの一覧を取得
		List<Task> taskList = taskService.findAll();		
		model.addAttribute("taskList", taskList);
		
		return "task/index";
	}
	
}


serviceクラス

package com.example.demo.service;

import java.util.List;

import com.example.demo.entity.Task;

/**
 * タスク関連のサービスを提供するインターフェースです。
 */
public interface TaskService {
	
    /**
     * すべてのタスクを取得します。
     *
     * @return タスクのリスト
     */
	List<Task> findAll();

}
package com.example.demo.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.entity.Task;
import com.example.demo.repository.TaskRepository;

/**
 * タスク関連のビジネスロジックを担当するサービスクラスです。
 * タスクの検索、保存、更新などの機能を提供します。
 */
@Service
public class TaskServiceImpl implements TaskService{

	@Autowired
	TaskRepository taskRepository;
	
	/**
	 * タスク一覧を取得するメソッドです。
	 *
	 * @return List<Task> タスク一覧。
	 */
	@Override
	public List<Task> findAll() {
		return taskRepository.findAll();
		}
}

repositoryクラス

package com.example.demo.repository;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.example.demo.entity.Task;
import com.example.demo.mapper.TaskMapper;


/**
 * タスク情報にアクセスするためのリポジトリクラスです。
 */
@Repository
public class TaskRepository {
	
	private final TaskMapper taskMapper;
	
    /**
     * コンストラクタ
     *
     * @param taskMapper タスクデータへのマッパー
     */
    public TaskRepository(TaskMapper taskMapper) {
        this.taskMapper = taskMapper;
    }

    /**
     * 全てのタスクを取得します。
     *
     * @return タスクのリスト
     */
    public List<Task> findAll() {
        return taskMapper.findAll();
    }

}

エンティティクラス


package com.example.demo.entity;

import java.time.LocalDateTime;

/**
 * タスクエンティティクラス
 */
public class Task {
    /**
     * タスクID(自動インクリメントされる一意の識別子)。
     */
    private int taskId;

    /**
     * タスクのタイトル。
     */
    private String title;

    /**
     * タスクの説明。
     */
    private String description;

    /**
     * タスクの締め切り日時。
     */
    private LocalDateTime deadline;

    /**
     * タスクのステータス(例: 1 - 未着手, 2 - 作業中, 3 - 完了)。
     */
    private int status;

    /**
     * ユーザーID(タスクを所有するユーザーの識別子)。
     */
    private Integer userId;

    /**
     * タスクの作成日時(デフォルトは現在の日時)。
     */
    private LocalDateTime createdAt;

    /**
     * タスクの更新日時(更新時に現在の日時に自動設定)。
     */
    private LocalDateTime updatedAt;

    /**
     * 削除フラグ(タスクが削除されたかどうかを示す)。
     */
    private Boolean deleteFlg;

    public int getTaskId() {
        return taskId;
    }

    public void setTaskId(int taskId) {
        this.taskId = taskId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public LocalDateTime getDeadline() {
        return deadline;
    }

    public void setDeadline(LocalDateTime deadline) {
        this.deadline = deadline;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public LocalDateTime getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(LocalDateTime createdAt) {
        this.createdAt = createdAt;
    }

    public LocalDateTime getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(LocalDateTime updatedAt) {
        this.updatedAt = updatedAt;
    }

    public Boolean getDeleteFlg() {
        return deleteFlg;
    }

    public void setDeleteFlg(Boolean deleteFlg) {
        this.deleteFlg = deleteFlg;
    }
}

mapperクラス

package com.example.demo.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.example.demo.entity.Task;

/**
 * タスクエンティティにアクセスするための MyBatis マッパーインターフェースです。
 */
@Mapper
public interface TaskMapper {
	
    /**
     * 全てのタスクを取得します。
     *
     * @return タスクのリスト
     */
    List<Task> findAll();
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.TaskMapper">
	
	<!-- タスクの全件取得 -->
	<select id="findAll" resultType="com.example.demo.entity.Task">
        SELECT * FROM task where deleteFlg = 0;
    </select>
    
</mapper>

html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>List page</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
  </head>
  <body>

    <nav class="navbar bg-body-tertiary">
        <div class="container-fluid">
          <span class="navbar-brand mb-0 h1">Practice</span>
        </div>
    </nav>

    <div class="container">
        <div class="px-4 pt-3 my-3 text-center border-bottom">
            <h1 class="display-4 fw-bold text-body-emphasis mb-3">ToDo Application</h1>
            <div class="col-lg-6 mx-auto">
              <div class="d-grid gap-2 d-sm-flex justify-content-sm-center mb-5">
                <a type="button" class="btn btn-primary btn-lg px-4 me-sm-3" href="/task/add">create</a>
              </div>
            </div>
        </div>

        <div class="px-4 pt-2 my-2 text-center">
            <table class="table table-striped">
                <thead>
                  <tr>
                    <th scope="col">#</th>
                    <th scope="col">Title</th>
                    <th scope="col">description</th>
                    <th scope="col">deadline</th>
                    <th scope="col">status</th>
                    <th scope="col">button</th>
                    <th scope="col">button</th>
                  </tr>
                </thead>
                <tbody>
                  <!-- Sample Data -->
                  <tr th:each="taskList : ${taskList}" th:object="${taskList}">
                    <th scope="row" th:text="*{taskId}">1</th>
                    <td th:text="*{title}">Title</td>
                    <td th:text="*{description}">description</td>
                    <td th:text="${#temporals.format(taskList.deadline, 'yyyy/MM/dd HH:mm')}">Formatted Deadline</td>
                    <td th:text="*{status == 1 ? '未着手' : status == 2 ? '作業中' : '完了'}">Status</td>
                    <td><a class="btn btn-primary" href="/task/edit/1">edit</a></td>
                    <td><a class="btn btn-primary" href="/task/delete/1">delete</a></td>
                  </tr>
                </tbody>
              </table>
        </div>
      </div>

      <footer class="py-3 my-4">
        <p class="text-center text-body-secondary">&copy; 2023 Company, Inc</p>
      </footer>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
  </body>
</html>
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0