👌

Springboot/Mybatis/RESTAPI/axios 編集中

2024/11/22に公開

ーーー
Intellij IDEA使用 設定→ラグイン→Mybatisxを有効に。
Springboot
java JDK17
Springboot ビルドツール:Mavenではなくbuild.gradle使用
Mybatisとは、データベースにアクセスするフレームワーク
MySQL
vscode  
※バックエンドとクライアントのhostが分かれている前提で進めます。

ディレクトリ構成
src/
 └── main/
      ├── java/
      │    └── pairWork/
      │         ├── api/
      │         │    ├── entity/
      │         │    ├── repository/
      │         │    ├── service/
      │         │    └── controller/
      │         └── ...
      └── resources/
           └── application.properties
           └── mapper/
                └── TaskMapper.xml

テーブル作成
CREATE TABLE task (
    id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
    task_name VARCHAR(255) NOT NULL COMMENT 'タスク名',
    task_detail TEXT COMMENT 'タスク詳細',
    status INT NOT NULL COMMENT 'ステータス',
    expiration_date DATE COMMENT '期限日',
    registration_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登録日',
    updated_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日',
    man_hour INT COMMENT '工数'
);
テーブルにテストデータとして値を格納
INSERT INTO task (task_name, task_detail, status, expiration_date, registration_date, updated_date, man_hour)
VALUES 
('タスク1', 'デザインを完成させる', 1, '2024-11-25', '2024-11-18 09:00:00', '2024-11-18 09:00:00', 8),
('タスク2', 'クライアントミーティング準備', 2, '2024-11-27', '2024-11-18 10:00:00', '2024-11-18 10:00:00', 4),
('タスク3', 'プロジェクト仕様書の確認', 1, '2024-11-30', '2024-11-18 11:00:00', '2024-11-18 11:00:00', 12),
('タスク4', 'バックエンドAPIの設計', 3, '2024-12-05', '2024-11-18 12:00:00', '2024-11-18 12:00:00', 15),
('タスク5', 'ユニットテストケース作成', 2, '2024-11-28', '2024-11-18 13:00:00', '2024-11-18 13:00:00', 6),
('タスク6', 'コードレビューの実施', 1, '2024-11-29', '2024-11-18 14:00:00', '2024-11-18 14:00:00', 3),
('タスク7', '開発サーバーのセットアップ', 3, '2024-12-01', '2024-11-18 15:00:00', '2024-11-18 15:00:00', 10),
('タスク8', 'UIモックアップの作成', 2, '2024-11-26', '2024-11-18 16:00:00', '2024-11-18 16:00:00', 5),
('タスク9', 'ログ収集システムの導入', 1, '2024-11-30', '2024-11-18 17:00:00', '2024-11-18 17:00:00', 8),
('タスク10', '月次レポート作成', 3, '2024-11-24', '2024-11-18 18:00:00', '2024-11-18 18:00:00', 7);

Taskmapper.xml
<?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.xmlファイルの宣言とDTDの指定。
     MyBatisで使用するためのファイルで、DTDによってXMLの構造を定義します。 -->

<!--<mapper namespace="com.example.mapper.TaskMapper">-->
    <mapper namespace="pairWork.api.repository.mapper.task.TaskMapper">
    <!-- <mapper>タグは、SQL文とJavaのMapperインターフェースを関連付けるルート要素。
         namespace属性に、対応するJavaのMapperインターフェースの完全修飾名を指定します。 -->

    <!-- タスクの条件検索 -->
    <!-- タスクをステータスや期限日で検索するSQLクエリを記述する -->

    <select id="findAll" resultType="pairWork.api.entity.Task">
        SELECT
        id,
        task_name,
        task_detail,
        status,
        expiration_date,
        registration_date,
        updated_date,
        man_hour
        FROM task
    </select>
entity
entity(モデル層)
package pairWork.api.entity;

import java.util.Date;//データ型
import lombok.Data;//Lombokの@Dataアノテーションをクラスに付けることで、以下のような基本的なコードを自動生成してくれます

@Data
public class Task {
    private int id;                        // タスクID
    private String taskName;               // タスク名
    private String taskDetail;             // タスク詳細
    private int status;                    // ステータス
    private Date expirationDate;           // 期限日
    private Date registrationDate;         // 登録日
    private Date updatedDate;              // 更新日
    private int manHour;                   // 工数
}
repository mapper(インターフェイス)

細かく条件IDなどの取得ではなく簡易なfindAll();でデータをとりあえず全て取得できるように全検索

TaskRepository.java
package pairWork.api.repository;

import java.util.List;
import pairWork.api.entity.Task;
import pairWork.api.entity.Todo;
import pairWork.api.service.output.task.TaskSearchServiceInput;

public interface TaskRepository {

    List<Task> findAll();

    void register(Task entity);
}

repositoryクラスをマッピングする理由は後ほど追記

Taskmapper.java
package pairWork.api.repository.mapper.task;

import java.util.List;
import org.apache.ibatis.annotations.Mapper;
//import org.apache.ibatis.annotations.Param;
import pairWork.api.entity.Task;

@Mapper
public interface TaskMapper {
    List<Task> findAll();
TaskDatasource

TaskDatasource が、TaskRepository インターフェースを実装している部分

TaskDatasource.java

package pairWork.api.repository.datasource;

import pairWork.api.entity.Task;
import pairWork.api.repository.TaskRepository;
import pairWork.api.repository.mapper.task.TaskMapper;

import java.util.List;

public class TaskDatasource implements TaskRepository {
    private static TaskMapper taskMapper;

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

    @Override
    public void register(Task entity) {

    }
}

・DTO

【入口】
【役割】TaskSearchRequestDtoは「タスク検索条件」をクライアントから受け取るための
DTO(データ転送オブジェクト) REST API がクエリパラメータで送信されたデータをこのクラスにマッピングします。


[クライアントリクエスト]
      |
      v
[クエリパラメータ] --> [TaskSearchRequestDto]


【service配下】
【役割】TaskDtoServiceOutput は、データベースから取ってきた
「タスク情報」を、他のプログラムで使いやすい形に整理するための「変換作業」をしています。


[データベース]
      |
      v
[生データ] --> [変換処理] --> [TaskDtoServiceOutput]



【出口】
【役割】TaskSearchResponseDto クラスは、
タスク検索APIのレスポンス(返り値)を表現するためDTO(データ転送オブジェクト)


[検索結果]
      |
      v
[整形処理] --> [TaskSearchResponseDto] --> [クライアントレスポンス]



【役割】TaskSearchResponseItemDtoは、タスク検索APIのレスポンスとして返却される
個々のタスク情報を表現するためのデータ転送オブジェクト(DTO)です

+--------------------------------------------------+
|                TaskSearchResponseItemDto        |
+--------------------------------------------------+
| - taskId: String                                 |  // タスクの一意識別子
| - title: String                                  |  // タスクのタイトル
| - description: String                            |  // タスクの詳細説明
| - status: String                                 |  // タスクの状態(例: 未着手、進行中、完了)
| - createdAt: DateTime                           |  // タスクの作成日時
| - updatedAt: DateTime                           |  // タスクの最終更新日時
| - assignedTo: String                             |  // タスクの担当者
| - dueDate: DateTime                             |  // タスクの期限
+--------------------------------------------------+

serviceフォルダのoutputフォルダ配下にtaskフォルダ「TaskDtoServiceOutput」ファイル
【役割】TaskDtoServiceOutput は、データベースから取ってきた「タスク情報」を、他のプログラムで使いやすい形に整理するための「変換作業」をしています。

TaskDtoServiceOutput.java
//DTOとは、データをそのまま使うんじゃなくて、サービス層で必要な情報だけを抜き出して、DTO(データ転送オブジェクト)っていう形式に変換して返してる
//目的:サービス層で使いやすい形に変換して出力用の情報を取り出す
/*
 *「SQLを実行するクラス」は DAO (Data Access Object)
 * 「テーブルのデータの情報を覚えておくためのクラス」は DTO (Data Transfer Object)
 */
import lombok.Builder;
import lombok.Data;
import pairWork.api.entity.Task;
import pairWork.api.repository.datasource.TaskDatasource;

@Data//@Data やけど、これはgetter、setter、toString、equals、hashCodeを自動で作ってくれるアノテーション
@Builder//@Builder は、ビルダーパターンを使ってオブジェクトを作るためのアノテーション
//Task エンティティから必要なフィールドを選んでDTOを作成
public class TaskDtoServiceOutput {
    private int id;
    private String taskName;
    private String taskDetail;

// TaskエンティティからDTOに変換するメソッド
public static TaskDtoServiceOutput fromEntity(Task task){
    return TaskDtoServiceOutput.builder()
            .id(task.getId()) // TaskからIDを取り出してセット
            .taskName(task.getTaskName()) // Taskからタスク名を取り出してセット
            .taskDetail(task.getTaskDetail())// Taskからタスク詳細を取り出してセット
            .build();//最後の .build() でそのオブジェクトを実際に作成して返している
    }
}
//entityToServiceOutput fromEntity

②controllerフォルダのInputフォルダ配下にtaskフォルダ「TaskSearchRequestDto」ファイル
【役割】TaskSearchRequestDtoは「タスク検索条件」をクライアントから受け取るための DTO(データ転送オブジェクト) REST API がクエリパラメータで送信されたデータをこのクラスにマッピングします。

TaskSearchRequestDto 
package pairWork.api.controller.input.task;

// Lombok を利用してコードを簡潔にするためのアノテーションをインポート
import lombok.AllArgsConstructor; // すべてのフィールドを引数に持つコンストラクタを自動生成
import lombok.Data;             // getter/setter、toString、equals、hashCodeを自動生成
import lombok.NoArgsConstructor; // 引数なしのデフォルトコンストラクタを自動生成

@AllArgsConstructor
@NoArgsConstructor
@Data
public class TaskSearchRequestDto {
  private Integer id;
  private String taskName;
  private Integer status;
  private String expirationFrom;
  private String expirationTo;
}

③,④controllerフォルダのoutフォルダ配下にtaskフォルダ「TaskSearchResponseDto」「TaskSearchResponseItemDto」ファイル

【役割】TaskSearchResponseDto クラスは、タスク検索APIのレスポンス(返り値)を表現するための**DTO(データ転送オブジェクト)

TaskSearchResponseDto.java
package pairWork.api.controller.out.task;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.Builder;
import lombok.Data;
import pairWork.api.entity.Task;

@Builder
@Data
public class TaskSearchResponseDto {
  private List<TaskSearchResponseItemDto> taskList;

  public static TaskSearchResponseDto of(List<Task> taskList) {
    return TaskSearchResponseDto.builder()
        .taskList(taskList.stream()
            // Task→TaskSearchResponseItemDtoに変換
            .map(TaskSearchResponseItemDto::of)
            // StreamになっているのをListに変換
            .collect(Collectors.toList()))
        .build();
  }
}

【役割】TaskSearchResponseItemDtoは、

TaskSearchResponseItemDto.java
package pairWork.api.controller.out.task;

import java.time.format.DateTimeFormatter;
import lombok.Builder;
import lombok.Data;
import pairWork.api.entity.Task;

@Builder
@Data
public class TaskSearchResponseItemDto {
  private int id;                        // タスクID
  private String taskName;               // タスク名
  private int status;                    // ステータス
  private String expirationDate;           // 期限日

  public static TaskSearchResponseItemDto of(Task task) {
    return TaskSearchResponseItemDto.builder()
        .id(task.getId())
        .taskName(task.getTaskName())
        .status(task.getStatus())
        .expirationDate(task.getExpirationDate().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")))
        .build();
  }
}

DTO
DTO.java
//serviceの実装
service(サービス層ビジネスロジック)
Taskservice.java
//serviceの実装
controller (ApiResult)
TaskContoroller.java
package pairWork.api.controller;


import java.util.List;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import pairWork.api.controller.input.task.TaskSearchRequestDto;
import pairWork.api.controller.out.ApiResult;
import pairWork.api.controller.out.task.TaskSearchResponseDto;
import pairWork.api.entity.Task;
import pairWork.api.service.TaskService;

@AllArgsConstructor
@RestController
public class TaskController {
    private final TaskService taskService;
    //restAPI-reactで呼び出す
    @GetMapping("/")
    public String index(){
        return "Hello HTML";
    }

    @RequestMapping(value = "/task/search", method = RequestMethod.GET)
    public ApiResult<TaskSearchResponseDto> search(TaskSearchRequestDto request) {
        List<Task> result = taskService.search(request);
        System.out.println(request);
        return ApiResult.of(TaskSearchResponseDto.of(result));
    }
}

webクライアント(axios)
task.tsx

Discussion