【はじめてのJava】ダッシュボードAPIを作成しよう!⑥試験回数遷移グラフ編

はじめに

こんにちは!マーケターから未経験のエンジニアに転身した社員Tです。
開発を始めて1年が経ち、サーバーサイド開発にも触れるようになりました。
今回は、Javaを使用したダッシュボードAPIの作成についてご紹介します。
これまでフロントエンド中心の開発を行っていた私が、サーバーサイドのAPI開発に挑戦していく様子をお届けします。

本記事の対象の方

  • サーバーサイド開発をこれから学びたい方
  • JavaでのAPI開発に興味がある方
  • ダッシュボードの作成を通じて、実践的な技術を学びたい方

Javaとは?

Javaは、非常に人気のあるオブジェクト指向プログラミング言語で、さまざまなアプリケーションの開発に広く使用されています。

詳しい説明については、過去のブログ記事をご参照ください。
⇒関連記事はこちら

環境構築

環境構築に関しては、内容が広範囲にわたるため、別の記事で詳細に解説する予定です。

ダッシュボードAPIの作成

今回作成したいのは、こんなダッシュボード画面です。ユーザーがコースを選択することで、自分の学習状況を一目で確認できるようにすることを目指しています。

全体の作成の流れとしては、下記の通りとなります。

  1. ユーザー情報を取得
  2. コース情報を取得
  3. 各グラフ描画に必要なデータを取得

今回は、3.各グラフ描画に必要なデータを取得するAPIの実装について詳しく触れていきたいと思います。5つグラフがありますが、左下のスコア遷移表の制作に取り掛かりたいと思います。

1. ユーザー情報の取得については、過去のブログ記事をご参考ください。
⇒関連記事はこちら

2. コース情報の取得については、過去のブログ記事をご参考ください。
⇒関連記事はこちら

試験情報取得APIの設計

エンドポイント

/top/getTrialExamHistory
  • HTTPメソッド: POST
  • リクエストボディ: ユーザーID、コースID
  • レスポンス: 模擬試験のスコアリスト

リクエスト例

{
    "courseId":"7",
    "userId":"175"
}

今回はパスパラメータではなく、リクエストボディからユーザーIDとコースIDを受け取る設計にしています。これにより、柔軟なデータの受け渡しが可能となります。

レスポンス例

{
    "trialExamHistoryList": [
        {
            "trialExamHistoryId": 63,
            "trialExamId": 1,
            "trialExamTimes": 5,
            "trialScore": 90,
            "userId": null
        },
        {
            "trialExamHistoryId": 69,
            "trialExamId": 1,
            "trialExamTimes": 2,
            "trialScore": 75,
            "userId": null
        },
        {
            "trialExamHistoryId": 72,
            "trialExamId": 1,
            "trialExamTimes": 1,
            "trialScore": 70,
            "userId": null
        }
    ],
    "errorMessageList": null
}

試験情報取得APIの実装

モデルクラスの作成

模擬試験履歴を表すエンティティクラスを作成します。このクラスはデータベースの trial_exam_history テーブルに対応し、各試験の履歴情報を管理します。

TrialExamHistoryEntity.java
 1package jp.co.smsdatatech.questiongenerate.entity;
2 
3import java.io.Serializable;
4import lombok.Data;
5 
6/**
7 * TrialExamHistoryテーブルEntity
8 */
9@Data
10public class TrialExamHistoryEntity implements Serializable {
11 
12 /** シリアルバージョンUID */
13 private static final long serialVersionUID = 1L;
14 
15 /** 試験履歴ID */
16 private Integer trialExamHistoryId;
17 
18 /** 試験ID */
19 private Integer trialExamId;
20 
21 /** 試験回数 */
22 private Integer trialExamTimes;
23 
24 /** 試験スコア */
25 private Integer trialScore;
26 
27 /** ユーザーID */
28 private Integer userId;
29}

MyBatisのMapper設定

MyBatisのMapperインターフェースを定義し、模擬試験履歴を取得するメソッドを宣言します。指定されたユーザーIDとコースIDに基づいて直近5回分の模擬試験履歴を取得しています。

TrialExamHistoryMapper.java
 1package jp.co.smsdatatech.questiongenerate.repository;
2 
3import java.util.List;
4import org.apache.ibatis.annotations.Mapper;
5import org.apache.ibatis.annotations.Param;
6 
7import jp.co.smsdatatech.questiongenerate.entity.TrialExamHistoryEntity;
8 
9/**
10 * TrialExamHistoryテーブルMapper
11 */
12@Mapper
13public interface TrialExamHistoryMapper {
14 
15 /**
16 * 指定されたユーザーID、コースIDに基づいて直近5回分の模擬試験履歴を取得する。
17 *
18 * @param userId ユーザーID
19 * @param courseId コースID
20 * @return 模擬試験スコアリスト
21 */
22 List<TrialExamHistoryEntity> selectTrialExamHistoryList(
23 @Param("userId") Integer userId,
24 @Param("courseId") Integer courseId);
25 
26 /**
27 * 指定したコースIDに基づいて、複数ユーザーの最新試験履歴を取得します。
28 *
29 * @param courseId コースID
30 * @return 最新の試験履歴リスト(各ユーザー1件)
31 */
32 List<TrialExamHistoryEntity> selectLatestTrialExamHistoryList(
33 @Param("courseId") Integer courseId);
34}

サービスクラスの作成

次に、サービス層でデータベースから直近5回分の模擬試験履歴を取得するメソッドを実装します。このメソッドは、ユーザーIDとコースIDをもとに、模擬試験履歴をデータベースから取得します。

TrialExamHistoryService.java
 1/**
2 * 模擬試験履歴リスト作成Service
3 */
4@Slf4j
5@Service
6public class TrialExamHistoryService {
7 
8 private final TrialExamHistoryMapper trialExamHistoryMapper;
9 
10 // コンストラクタインジェクションを使用
11 @Autowired
12 public TrialExamHistoryService(TrialExamHistoryMapper trialExamHistoryMapper) {
13 this.trialExamHistoryMapper = trialExamHistoryMapper;
14 }
15 
16 /**
17 * 指定されたユーザーID、コースIDに基づいて直近5回分の模擬試験履歴を取得する。
18 *
19 * @param userId ユーザーID
20 * @param courseId コースID
21 * @return 模擬試験履歴リスト
22 */
23 @Transactional(readOnly = true)
24 public List<TrialExamHistoryEntity> selectTrialExamHistoryList(Integer userId, Integer courseId) {
25 
26 log.debug("ユーザーID {} のコースID {} の模擬試験履歴を取得します。", userId, courseId);
27 
28 // データベースから模擬試験履歴を取得
29 List<TrialExamHistoryEntity> trialExamHistoryList = trialExamHistoryMapper.selectTrialExamHistoryList(userId, courseId);
30 log.debug("取得した模擬試験履歴リスト: {}", trialExamHistoryList);
31 
32 return trialExamHistoryList;
33 }
34}

コントローラークラスの作成

@PostMapping アノテーションを使って、/top/getTrialExamHistory というエンドポイントを定義します。このエンドポイントは、ユーザーIDとコースIDを含むフォームを受け取り、バリデーションエラーがあればエラーメッセージを返します。

TopRest.java
 1/**
2 * 模擬試験履歴を取得.
3 *
4 * <h3>機能概要</h3>
5 * ユーザーIDおよびコースIDに基づいて、模擬試験履歴を取得する。
6 *
7 * <h3>処理フロー</h3>
8 * 1. リクエストデータのバリデーション<br>
9 * 2. サービスクラスを呼び出して、模擬試験履歴を取得<br>
10 * 3. 履歴が見つからない場合はエラーメッセージを設定<br>
11 * 4. 模擬試験履歴またはエラーメッセージリストを返却
12 *
13 * @param form ユーザーIDとコースIDのリクエストフォーム
14 * @param result バリデーション結果やエラーを格納
15 * @return 模擬試験履歴リスト TrialExamHistoryResponseForm
16 */
17@PostMapping("/top/getTrialExamHistory")
18public TrialExamHistoryResponseForm getTrialExamHistory(
19 @RequestBody @Validated({ ValidationGroups.Step1.class, ValidationGroups.Step2.class }) UserIdForm form,
20 BindingResult result) {
21 log.debug("TrialExamHistoryRest (getTrialExamHistory) 呼び出し開始");
22 
23 TrialExamHistoryResponseForm responseForm = new TrialExamHistoryResponseForm();
24 List<String> errorMessageList = new ArrayList<>();
25 
26 // バリデーションエラーがある場合、詳細なエラーメッセージを追加
27 if (result.hasErrors()) {
28 for (FieldError fieldError : result.getFieldErrors()) {
29 String errorMessage = messageSource.getMessage(fieldError, Locale.getDefault()); // Localeを指定
30 errorMessageList.add(errorMessage);
31 }
32 responseForm.setErrorMessageList(errorMessageList);
33 return responseForm;
34 }
35 
36 Integer userId = Integer.parseInt(form.getUserId());
37 Integer courseId = form.getCourseId();
38 
39 // 模擬試験履歴リストを取得
40 List<TrialExamHistoryEntity> trialExamHistoryList = trialExamHistoryService.selectTrialExamHistoryList(userId,
41 courseId);
42 
43 // リストが取得できなかった場合
44 if (CollectionUtils.isEmpty(trialExamHistoryList)) {
45 errorMessageList
46 .add(messageSource.getMessage(ErrorMessages.TRIAL_EXAM_HISTORY_NOT_FOUND, null,
47 Locale.getDefault()));
48 responseForm.setErrorMessageList(errorMessageList);
49 log.debug("模擬試験履歴が見つかりませんでした");
50 return responseForm;
51 }
52 
53 responseForm.setTrialExamHistoryList(trialExamHistoryList);
54 log.debug("TrialExamHistoryRest (getTrialExamHistory) 呼び出し終了");
55 
56 return responseForm;
57}

実装内容の振り返り

今回はシンプルに直近5回分の試験履歴を取得する実装でしたが、今後はキャッシュの活用や、SQLクエリ内で全件取得後に絞り込むのではなく、最初から直近5回分だけを取得するように修正することで、データベースへの負荷を軽減でき、より実用的に利用できるようになると感じました。

まとめ

シンプルな実装ではありましたが、SQLの書き方やキャッシュの導入余地など改善点も見えてきました。今後はパフォーマンスを意識した設計を取り入れ、より実用的なAPIに仕上げていきたいと思います。

その他のイベント・セミナー 一覧へ