はじめに
学習時間を管理するアプリをSpringBootで作成している過程で、Spring JPAを使用してクエリを作成していました。使用したカスタムクエリを記載し、書き方をまとめておこうと考えました。テーブル属性が「datetime」型を持っているテーブルを操作したい場合に役に立つかなと思います。
基礎的な部分
まずは基礎的な部分を学んでいきたいと思います。
正式名称は「Spring Data JPA」のJPQL(Java Persistence Query Language)です。そのため、通常のSQLとは書き方が違うという事を抑えておきます。
使用しているテーブル
DBによっても書き方が変わってくる可能性あります。その点は認識して頂けるとありがたいです。筆者が使用しているのは「MySQL」です。
では、使用しているテーブルから整理していきます。テーブル名は「study_times」で属性は「datetime」を主に持っています。
Table:study_times
Columns:
study_times_id | bigint AI PK |
start_time | datetime |
end_time | datetime |
created_at | datetime |
updated_at | datetime |
is_learning | tinyint(1) |
Javaのエンティティの定義
Javaでこのテーブルを使用するには、エンティティclassを用意する必要があります。上記のテーブルを反映させるために以下のようなコードを書きます。アノテーションなどに関してはJavaの記述になるため、すみませんが割愛します。
package com.app.progTrack.entity;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Entity
@Table(name = "study_times")
@Data
public class StudyTime {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "study_times_id")
private Long studyTimesId;
@Column(name = "start_time")
private LocalDateTime startTime;
@Column(name = "end_time")
private LocalDateTime endTime;
@Column(name = "created_at", insertable = false, updatable = false)
private Timestamp createdAt;
@Column(name = "updated_at", insertable = false, updatable = false)
private Timestamp updatedAt;
@Column(name = "is_learning")
private Boolean isLearning;
}
- 「@Table(name = “study_times”)」でDBで作成したテーブルを指定してます。
なので、定義上「stydt_times == StudyTime」という認識になる事が大前提となります。
まずは以下を抑えておきます。
- エンティティを対象とする事
JPQLはエンティティ(StudyTime)を直接操作する事が出来ます。「SELECT s FROM StudyTime s」と記述する事で、エイリアスを指定する事が出来ます。これにより「StudyTime」のすべてのプロパティを取得できます。
JPQLとSQLを見比べてみましょうか!
-- JPQL
SELECT s
FROM StudyTime s
-- 通常のSQL
SELECT *
FROM StudyTime AS s
- 自動マッピング
Spring Data JPA はクエリ結果を自動的にエンティティにマッピングしてくれます。SELECT句で具体的なカラム名を指定せずとも、エンティティのプロパティに基づいて結果が整形されます。
従って、特定のプロパティを取得したい場合は明示的にSELECT句で指定する必要があります。
SELECT句を指定していない例
SELECT句を明示的に指定していない例を見てみましょう。こちらのクエリは、当月の学習月と学習日を取得するのに作成したクエリとなります。
@Query("SELECT s FROM StudyTime s WHERE MONTH(s.startTime) = :month AND YEAR(s.startTime) = :year")
List<StudyTime> findAllByMonthAndYear(@Param("month") int month, @Param("year") int year);
- WHERE句
月の値を取得するために「:month」を指定し、日を値を取得するために「:year」とします。
こちらを使用してJava側のメソッドの引数で「@Param」として指定する事でメソッドを作成する事が出来ます。
ちなみに通常のMySQLで作成したクエリは以下になります。見比べてみましょう!
SELECT start_time, end_time
FROM study_times
WHERE MONTH(start_time) = MONTH(CURRENT_DATE())
AND YEAR(start_time) = YEAR(CURRENT_DATE());
SELECT句を指定している例
SELECT句で指定している例を見てみましょう。以下のクエリでは当月の学習時間を把握するため、学習時間を集計するクエリです。この場合集計関数である「SUM」を使用するため、明示的にプロパティの指定が必要になるという例になります。
// 学習時間を計算する
@Query("SELECT SUM(TIMESTAMPDIFF(MINUTE, s.startTime, s.endTime)) FROM StudyTime s WHERE MONTH(s.startTime) = :month AND YEAR(s.startTime) = :year AND s.endTime IS NOT NULL")
Long findTotalStudyTimeByMonth(@Param("month") int month, @Param("year") int year);
「IS NOT NULL」に関しては、アプリケーション作成の都合上、エラーとなるため付与していますので、付与しないのが通常のクエリとなります。
おわりに
アプリを作成している中でSELECT句に関して少し、疑問になったのでまとめてみました。クエリ単体ではなくJavaのアプリとして、どのように形成していくか気になる方は以下の記事で作成してますので、是非目を通してみてくださいね!
お読みいただきありがとうございました。
コメント