Spring Data JPAとは
JPA、Spring Data、Spring Data JPA
JPA(Java Persistence API)は、2006年にリリースされたJava EE 5で仕様が定められているJava標準のORM(Object-Relational Mapping/Mapper)で、Java でオブジェクトとリレーショナルデータベースをやり取りするためのAPIです。オブジェクト指向のデータベース操作ができるため、SQL文を直接書かなくても、Javaのオブジェクトとしてデータを扱えるようになります。
JPAは仕様の定義をしているだけで、実装は提供していません。JPAの実装は、ORMを開発しているベンダーによって(Hibernate等)、参照実装として提供されています。
また、正しく説明すると、JPAは「データの永続化」に関する機能です。永続化とは、プログラムの終了やコンピューターの電源断後も消滅せずに存続する性質のことで、オブジェクトの状態や関連をデータベースに保存し復元する機能を指します。
Spring Dataは、データベース操作を簡単にするためのフレームワークで、データアクセスのための定型コードを削減することを目的としたSpringサブプロジェクト群の1つです。Repositoryの実装を最小限にするための機能を提供します。
Spring Data JPAは、上記2つを組み合わせたもので、JPAを用いてデータアクセスを行うためのRepository実装にかかる負荷を最小限にしてくれます。
Spring Data JPAを用いると、Repositoryのインターフェースを作成してアノテーションやメソッドを定義するだけで、データの参照・更新を行えるようになります。つまり、開発者は実行クエリを直接考えることなく、必要なファイル等の設定をするだけで具体的な内部実装はフレームワークに任せられるということです。Spring Bootにおける実装はデフォルトではHibernateが担っています。
更に、独自の機能も追加されており、データを作成・更新したユーザーや日時などの監査情報を自動的にバインドする機能等もあります。
Spring Data JPAの使用方法
前の単元のおさらいになりますが、Spring Data JPAを利用する上で必ず必要なものは以下の2種です。
Entity(エンティティ)
エンティティ(Entity)は、Object-Relational Mappingで言うところのオブジェクトです。データベース内でのデータはテーブルに保存されているレコードで、エンティティはこの1つ1つのレコードをJavaオブジェクトとして保管できるものです。
Repository(リポジトリ)
Repository(リポジトリ)とは、データベースアクセスのための基本的な手段を提供するインタフェースです。
Spring Data JPAでは、JpaRepository(やCrudRepository※)を継承するインタフェースを作るだけで、基本的なDB操作を実行するメソッドを用意してくれます。
※JpaRepositoryはCrudRepositoryを拡張したものですので、本マニュアルでは基本的にJpaRepositoryを使用します。
実践 – Entity
様々な処理を使うにあたって、テーブルにカラムを追加し、Entityに追記しましょう。
①カラムの追加
以下のSQL文を実行してください。「登録日時」「更新日時」を保存するためのカラムです。一般的なシステムにおいて、登録日時や登録者などの監査情報は、管理上かならず登録すべきものです。今後テーブルを作成する際は、登録・更新日時のカラムも含めるようにしましょう。
ALTER TABLE employees ADD created_at TIMESTAMP WITH TIME ZONE;
ALTER TABLE employees ADD modified_at TIMESTAMP WITH TIME ZONE;
②Entityの追記
【com/cmps/spring/entity/Employee.java】
package com.cmps.spring.entity;
import java.io.Serializable;
import java.time.LocalDateTime;////追記
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.Id;
import jakarta.persistence.Column;
import jakarta.persistence.SequenceGenerator;////追記
import jakarta.persistence.GeneratedValue;////追記
import lombok.Data;
import lombok.NoArgsConstructor;////追記
import org.springframework.data.annotation.CreatedDate;////追記
import org.springframework.data.annotation.LastModifiedDate;////追記
import org.springframework.data.jpa.domain.support.AuditingEntityListener;////追記
@Entity
@Data
@NoArgsConstructor////追記
@EntityListeners(AuditingEntityListener.class)////追記
@Table(name = "employees")
public class Employee implements Serializable {
// ID
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// 従業員コード
@Column
private String code;
// 名前
@Column
private String name;
// 年齢
@Column
private Integer age;
////以下、追記
// 登録日時
@Column(updatable = false)
@CreatedDate
private LocalDateTime createdAt;
// 更新日時
@Column
@LastModifiedDate
private LocalDateTime modifiedAt;
// 引数ありコンストラクタ
public Employee(String code, String name, Integer age) {
this.code = code;
this.name = name;
this.age = age;
}
}
classの前に追加された2つのアノテーションについて
・@NoArgsConstructorは、lombokのアノテーションで、引数無しのコンストラクタを作成してくれます。
・@EntityListeners(AuditingEntityListener.class)を記載すると、レコードの作成・更新時の情報登録する機能を有効にします。
主キー・idカラムへの追記内容
@GeneratedValue(strategy = GenerationType.IDENTITY)
・@GeneratedValueは、自動採番のカラムにつけるアノテーションです。自動採番の設定をすることにより、データ保存時にSpringの記述上で具体的なidを指定しなくても自動採番ルールに則って登録してくれます。@GeneratedValueの設定値はDBごとに差異がありますので注意が必要で、今回はOracle DBでIDENTITYで指定した場合の記載になっています。(strategy = GenerationType.IDENTITY)
参考サイト:JPAの主キー(プライマリキー)マッピング
(補足)SEQUENCEで設定する場合の記述
@GeneratedValueの他に、@SequenceGeneratorをセットで使用する必要があります。
「EMP_SEQ01」というSEQUENCEを作成して指定した場合、下記のように設定します。
@SequenceGenerator(name="EMP_SEQUENCE_GENERATOR", schema="sample", sequenceName="sample.EMP_SEQ01", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="EMP_SEQUENCE_GENERATOR")
・シーケンスオブジェクトを明示的に指定するために、@SequenceGeneratorと@GeneratedValueを対応させて記載します。
SEQUENCE名を指定し(sequenceName="EMP_SEQ01")、allocationSizeは自動増分する値を記載します。
@SequenceGeneratorのname="EMP_SEQUENCE_GENERATOR"の値を、@GeneratedValueのgenerator="EMP_SEQUENCE_GENERATOR"と一致させることで紐づけます。この”EMP_SEQUENCE_GENERATOR”は、一意かつ互いに一致していればよく命名のルールはありません。
登録日時・更新日時
・登録日時、更新日時のカラムに対応するフィールド変数createdAt、modifiedAtを追加しました。それぞれテーブル上のカラム名はスネークケース(○○_at)ですが、JPA(Hibernate)の仕様により、キャメルケースのフィールド名(○○At)はSQL文生成時に自動でスネークケースに変換されます。今回のようにそのルールに則る場合は、特に@Columnのname属性を記載する必要はありません。
・アノテーション@CreatedDate、@LastModifiedDateは、データ登録時にそれぞれ作成日時、登録日時を自動で実行SQLに含めて登録してくれます。
created_atについては最初の一度のみ日時を保存したいので、@Column内でupdatable = falseとすることで更新時にnullが入るのを防ぎます(記載必須)。
引数ありコンストラクタ
・これまで記載してきた通り、id、createdAt、modifiedAtは、Spring側でSQLを生成時に自動で登録データを足してくれます。
裏を返せば、それ以外のcode、name、ageについては登録するデータをオブジェクトに格納する必要があります。今回のようにコンストラクタを作成して使用するほか、セッターメソッドでももちろん可能です。
その他、NOT NULLやユニーク制約をフィールドに対して課している場合は、@Columnアノテーションの属性に指定します。
公式:Column (Jakarta EE 8 Specification API) – Javadoc
参考サイト:JPAのフィールドとカラムのマッピング
③@SpringBootApplicationへの追記
作成・更新日時の自動登録をするためには、アプリの起動に携わる@SpringBootApplicationのファイルへの設定が必要です。パッケージ直下のファイルに追記します。
【com/cmps/spring/Spring○○Application.java】
package com.cmps.spring;
import java.util.TimeZone;////追記
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;////追記
import jakarta.annotation.PostConstruct;////追記
@EnableJpaAuditing////追記
@SpringBootApplication
public class Springboot2412Application {
public static void main(String[] args) {
SpringApplication.run(Springboot2412Application.class, args);
}
////以下、追記
@PostConstruct
public void init() {
TimeZone.setDefault(TimeZone.getTimeZone("JST"));
}
}
・@EnableJpaAuditing:レコードの作成、更新時の情報設定を有効にします。
・@PostConstruct:依存性注入後に初期化のために実行する必要のあるメソッドに対して使用します。ここではタイムゾーンの設定をSpringBootに与えるために必要な記載となります。
・TimeZone.setDefault(TimeZone.getTimeZone("JST"));spring bootのタイムゾーンをJSTに設定しています。(デフォルトはUCT)
実践 – Repositoryの基本メソッド
Repositoryを作成すると自動で実装される基本のメソッド(JpaRepositoryが提供するメソッド)から特によく使うメソッドを紹介します。
findById(id) ~IDに一致するレコードの取得~
ID に一致するエンティティを検索し、Optional<Entity>に取得できます。1つ前の単元で紹介したメソッドです。
IDに一致するエンティティが見つからなかった場合、Optionalにはnullがセットされます。
Entityオブジェクトを取り出すには、後述のget()またはorElse()メソッドを使用する必要があります。
Optional<Employee> employeeOpt = employeeRepository.findById(1);
Optional型に対しては、以下のメソッドが使用できます。値=オブジェクト(Entity)と読み代えてください。
| isEmpty() | 値が存在しない場合はtrue、それ以外の場合falseを返します。 |
| isPresent() | 値が存在する場合はtrue、そうでない場合はfalseを返します。 |
| get() | 値がある場合は値を返し、そうでない場合はNoSuchElementExceptionをスローします。 |
| orElse(T other) | 値が存在する場合は引数に指定した値(nullも指定可能)を返し、そうでない場合はotherを返します。 |
Java公式:Optional (Java SE 11 & JDK 11 )
Entityを返すgetReferenceById(id)というメソッドもありますが、findByIdと違いnullを許可せず例外をスローする可能性が高いため、findByIdの方が使いやすいです。
上述の通り、findByIdが返すOptional型には取得できたかどうかを確認するメソッド(isEmpty()、isPresent())がある点でもそう言えるでしょう。
findAll() ~全件取得~
エンティティ全件(全データ)を、Iterable<Entity>で取得できます。SQL文で書くとSELECT * FROM emplyees;です。
Iterableとは、java.langパッケージに属し、拡張for文(for-each文)が使えるようにするためのインタフェースです。Iterableを継承したものとしてCollection、を継承したものとしてListがある、といった関係性です。ArrayListやListと似たようなイメージで、中にオブジェクトを内包できる、ことが理解できればここでは大丈夫です。
employeesテーブルのデータを取得し、Viewに表示する例を示します。
実装例
【com/cmps/spring/controller/EmployeeController.java】に追記
/**
* employeesテーブルのデータ全件を取得しブラウザに表示する
*
* @param model Model
* @return "employee/list" String viewファイル
*/
@GetMapping("/all")
public String getAll(Model model) {
Iterable<Employee> employees = employeeRepository.findAll();
model.addAttribute("employees", employees);
return "employee/list";
}
【src/main/resources/templates/employee/list.html】を新規作成
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spring Data JPA(findAll)</title>
</head>
<body>
<h1>employeesテーブル全件</h1>
<table>
<thead>
<tr>
<th>id</th>
<th>code</th>
<th>name</th>
<th>age</th>
<th>createdAt</th>
<th>modifiedAt</th>
</tr>
</thead>
<tbody>
<tr th:each="employee : ${employees}" th:object="${employee}">
<td th:text="*{id}"></td>
<td th:text="*{code}"></td>
<td th:text="*{name}"></td>
<td th:text="*{age}"></td>
<td th:text="*{(createdAt eq null)? '' : #temporals.format(createdAt, 'yyyy/MM/dd HH:mm')}"></td>
<td th:text="*{(modifiedAt eq null)? '' : #temporals.format(createdAt, 'yyyy/MM/dd HH:mm')}"></td>
</tr>
<tr th:if="${employees.size == 0}">
<td colspan="6">該当するデータがありません。</td>
</tr>
</tbody>
</table>
</body>
</html>
View側
・Controller側から受け取ったIterable型のemployeesをth:eachで回してEmployeeオブジェクトを取り出し、データを表示しています。
createdAt、modifiedAtについては、三項演算子で記述することで、SQL文で最初に登録したデータについてはnullのため何も表示せず、今後Spring Bootアプリケーションから登録したデータは日時が整形された状態で表示されるようにしています。
確認してみましょう。
URL:http://localhost:8080/emp/all
CSSの適用状況やブラウザにより多少の見た目は異なるかもしれませんが、DBから取得したデータ全件が表示出来ているでしょう。

finadAllの並び替え
findAll()メソッドで取得した結果を並び替え(ORDER BY)する方法もあります。基本的には指定しましょう。import org.springframework.data.domain.Sort; 左記のimport文を追加し、Iterable employees = employeeRepository.findAll(Sort.by(Sort.Direction.ASC, "id")); findAll()メソッドの引数に記述します。
上記の場合ですと、”id”カラムで昇順(ASC)という指定になります。カラム名、ASC/DESCを変更すればソートの仕方を変更できます。
save(entity) ~データの保存~
引数に指定されたエンティティ1件を保存します。SQL文で言うと、INSERT、UPDATE文にあたります。
INSERTになるかUPDATEになるかはエンティティの主キーによって解決されます。(DBに存在するIDであればUPDATE、なければINSERT)
実装例
【com/cmps/spring/controller/EmployeeController.java】に追記
/**
* employeesテーブルにデータを登録する
*
* @param model Model Model情報
* @return "redirect:/emp/all" String 一覧表示画面へリダイレクト
*/
@GetMapping("/save")
public String save(Model model) {
// 新規登録する場合
Employee employee = new Employee("0008", "佐藤", 29);
employeeRepository.save(employee);
// 更新する場合
//Employee employee = employeeRepository.findById(8).get();
//employee.setAge(51);
//employeeRepository.save(employee);
return "redirect:/emp/all";
}
・新規登録する場合と更新する場合の2パターンを記載しています。
・return "redirect:/emp/all";と記載していますが、”redirect/(URL)“の形式で記載することで、別のGETメソッドにアクセスし直す(リダイレクトする)ことが出来ます。ここでは/emp/all = 1つ前のgetAll()アクションに転送されるので、最終的なブラウザの画面表示はemployeesテーブルの一覧データ表示になります。一覧データが表示されることで、データがどのように変更されたか確認しやすいと思います。
新規登録
まず、上記のコードのまま(「// 更新する場合」 のコード3行をコメントアウトした状態で)、URLにアクセスしてください。
URL:http://localhost:8080/emp/saveEmployee employee = new Employee("0008", "佐藤", 29);で引数ありコンストラクタによりオブジェクトを生成しています。idは指定していないので初期値nullのままなので、DBに存在しないエンティティと判断されINSERTメソッドが実行されます。Entityにて自動採番の設定をしているので、自動でidの値を挿入したうえでINSERT処理が実行されます。
更新
「// 新規登録する場合」 のコード2行をコメントアウトし、
「// 更新する場合」 のコード3行をコメント解除した上で、再度アクセスします。
URL:http://localhost:8080/emp/saveEmployee employee = employeeRepository.findById(8).get();で既存のデータをオブジェクトとして取得し、employee.setAge(51);setterメソッドで年齢を変更しています。id=8はDBに存在するエンティティと判断され、UPDATEメソッドが実行されます。
delete(entity)、deleteById(id) ~データの削除~
deleteメソッドは指定したエンティティ1件を、deleteByIdメソッドは指定されたidを持つエンティティ1件を削除します。
/**
* employeesテーブルからデータを削除する
*
* @param model Model Model情報
* @return "redirect:/emp/all" String 一覧表示画面へリダイレクト
*/
@GetMapping("/delete")
public String delete(Model model) {
// 指定したentityを削除
Employee employee = employeeRepository.findById(9).get();
employeeRepository.delete(employee);
// IDを元にentityを削除
//employeeRepository.deleteById(10);
return "redirect:/emp/all";
}
削除したい対象が既にない場合は例外をスローする可能性があるので、数値は必要に応じて変更して実行してみてください。
公式:
JpaRepository (Spring Data JPA Parent API) – Javadoc
CrudRepository (Spring Data Core API) – Javadoc(JpaRepositoryの継承元のため、CrudRepositoryのメソッドも使用可能)
自動実装のクエリメソッド(メソッド名から自動生成)
Repositoryにおけるクエリメソッドとは、Repository内に宣言するだけでメソッドの名前から自動実装されるメソッドです。
具体的な例で見ていきましょう。
名前列に対するあいまい検索を実装します。
【com/cmps/spring/repository/EmployeeRepository.java】に追記
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
/**
* 名前フィールドに引数の文字列を含むデータを取得する(あいまい検索)
* @param name String 名前
* @return Iterable<Employee> Employeeのコレクション
*/
Iterable<Employee> findByNameContaining(String name);
}
【com/cmps/spring/controller/EmployeeController.java】に追記
/**
* employeesテーブルの名前フィールドからあいまい検索し表示する
*
* @param model Model Model情報
* @return "employee/list" String viewファイル
*/
@GetMapping("/name-fuzzy")
public String getNameContaining(Model model) {
Iterable<Employee> employees = employeeRepository.findByNameContaining("田");
model.addAttribute("employees", employees);
return "employee/list";
}
いかがでしょうか。あいまい検索の結果がブラウザに出力できたと思います。
所定のキーワードを組み合わせ、適切な引数や戻り値を指定したメソッドをRepository内に宣言するだけで、このようにSpring Data JPA内部で自動実装してくれるのです。
クエリメソッド命名の具体的なルールを見ていきましょう!
件名キーワード(findBy、countBy)
どんな処理を行うのかを示すキーワードです。よく使う2つを紹介します。
| キーワード | 説明 | 返り値の型 |
| findBy○○ ※ | 検索する(SELECT) | 1件検索の場合:Entityクラス 複数行想定される場合:Iterable<Entity> |
| countBy○○ | 件数をカウントする(SELECT count(*)) | long型 |
※findBy○○については、返り値が複数行でIterableの場合はfindAllBy○○とし、1件の場合と明示的に分けることもあります。どちらの命名でも処理としては実現できます。
引数は、条件となるフィールド毎に用意します。ただし、条件が多い場合は、条件をまとめたDTOを用意することも可能です。
findByNameContaining(String name) の例で言えば、「名前列に含まれるキーワード」を引数に渡したいのでString型としています。
メソッド名の後半の○○部分は、SQL文で言うWHERE句にあたります。検索条件となるフィールドの物理名と、条件キーワードを指定します。
条件にあたる述語キーワードと修飾子
前述の○○部分は、「フィールド名 + 述語キーワード」の形が基本です。
先ほどの例 findByNameContaining(String name); で言えば、フィールド名はName、述語キーワードはContainingです。
・フィールド名には、Entityに定義したフィールド変数が使用でき、メソッド名として組み合わせるために1文字目をキャメルケース(大文字)にします。
例えば、age列に関する絞り込みであれば、findByAge…といった命名になります。
・述語キーワードのうち、よく使いそうなものと、メソッドの作成例を示します。
| 述語キーワード | 意味 | 使用例 |
| Is, Equals Not | 一致する、等しい 一致しない、ではない | 名前列がnameと一致するデータを取得 Employee findByNameEquals(String name); 年齢がageと等しいデータを取得 Iterable<Employee> findByAgeIs(int age); |
| IsEmpty, Empty IsNotEmpty, NotEmpty | 値が空である 値が空でない | 名前が空でないデータを取得 Iterable<Employee> findByNameNotEmpty(); |
| NotNull, IsNotNull Null, IsNull | NULLでない NULLである | コードが空でないデータを取得 Iterable<Employee> findByCodeIsNotNull(); |
| LessThan / GreaterThan LessThanEqual / GreaterThanEqual | 未満 / 超過 以下 / 以上 | 年齢がage以下のデータを取得 Iterable<Employee> findByAgeLessThanEqual(int age); |
| Like StartingWith / EndingWith / Containing | Likeには部分一致(ワイルドカードを含めた引数を渡す) 前方一致 / 後方一致 / 部分一致 | 名前にkeywordを含むデータを取得 Iterable<Employee> findByNameContaining(String keyword); |
| Between | SQLのBETWEENと同じ | 年齢がyounger~olderに当てはまるデータの件数を取得 long countByAgeBetween(int older, int younger); |
And、Orを組み合わせることで複数条件で検索することもできます。
| キーワード | 使用例 |
| And | 名前が空でないかつ年齢がage以下のデータの件数を取得 long countByNameNotEmptyAndAgeLessThanEqual(int age); |
| Or | 年齢がage1以下またはage1より大きいデータを取得 Iterable<Employee> findByAgeLessThanEqualOrAgeGreaterThan(int age1, int age2); |
| OrderBy(フィールド名)Asc/Desc | 名前にkeywordを含むデータをidの降順で取得 Iterable<Employee> findByNameContainingOrderByIdDesc(String keyword); |
公式:
JPA クエリメソッド :: Spring Data JPA – リファレンス
リポジトリクエリキーワード :: Spring Data Commons – リファレンス
参考サイト:
【Spring Data JPA】自動実装されるメソッドの命名ルール – Qiita
見ていただくと分かると思いますが、自動実装のクエリメソッドはメソッド名が長くなりがちです。複雑な処理の実装には不向きですし、集計関数については表現できません。
@Queryアノテーションでのメソッド定義
@Queryアノテーション(org.springframework.data.jpa.repository.Query)を使用すると、メソッドに定義したいクエリ内容を、
素のSQLないしJPQL(JavaPersistenceQueryLanguage、JPAで使用するSQLライクなクエリ言語)で記述することが出来ます。
JPQLで記述した例を以下に示します。
employeesテーブルに対し、名前でグループ分けし、Having句で平均年齢が30以上の名前を取得する処理です。
【com/cmps/spring/repository/EmployeeRepository.java】に追記
import org.springframework.data.jpa.repository.Query;////追記
import org.springframework.data.repository.query.Param;////追記
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
////以下追記
/**
* 名前列でグループ分けしたとき、平均年齢がage以上の名前
* @param age 検索したい平均年齢
* @return Iterable<String> 名前のリスト
*/
@Query("SELECT e.name FROM Employee e GROUP BY e.name HAVING AVG(e.age) >= :age")
Iterable<String> getNamesHavingAveAgeOlder(@Param("age") int age);
}
メソッドの直前の@Queryアノテーション内に、実行したい処理をJPQLで記述しています。見ての通り基本の書き方はSQLと大きく変わりません。
ただし、JPQLの記述のルールとして、以下のようなものがあります。
①FROM句には、テーブル名ではなく、「扱うエンティティ+エイリアス(別名)」を書く(FROM Employee e)
②列名は「エイリアス.列名」の形で記述する(e.name)
③SELECT句にエイリアスを指定した場合、戻り値はエンティティになる(例:SELECT e FROM~)
④「:変数名」という形式でプレースホルダを定義し、(HAVING AVG(e.age) >= :age)
@Paramアノテーションを使用すると、引数と紐づけることができる。(@Param("age") int age)
素のSQLを使用したい場合@Query(value = "SELECT name FROM employees GROUP BY name HAVING AVG(age) >= :age", nativeQuery = true)
上記のようにnativeQuery属性をtrueにすることで使用できます。引数1つだけであればvalue=は省略可能ですが、他の属性を指定する場合は明示する必要があります。
Controller側での確認例
Controller側ではメソッドを呼び出して使用しているだけで、使い方はこれまでのメソッドと変わりません。
【com/cmps/spring/controller/EmployeeController.java】に追記
/**
* employeesテーブルに対する集計関数の結果をコンソールに表示
*
* @param model Model Model情報
* @return "redirect:/emp/all" /emp/allへリダイレクト
*/
@GetMapping("/totalling")
public String getTotallingResult(Model model) {
// 平均年齢が30以上の名前を取得
Iterable<String> employees = employeeRepository.getNamesHavingAveAgeOlder(30);
// for文で回してコンソールに出力
for (String name : employees) {
System.out.print(name + ", ");
}
return "redirect:/emp/all";
}
参考:
JavaEE7をはじめよう(4) – JPAクエリ(その1) JPQL – エンタープライズギークス (Enterprise Geeks)
Spring Data JPA でのクエリー実装方法まとめ – Qiita
6.3. データベースアクセス(JPA編)
実行SQLを確認する方法
SQL文を確認したい場合、以下の設定でコンソールに実行SQLを出力させることができます。
(設定後、プロジェクト更新してください。)
【src/main/resources/application.properties】
## SQLログ出力(下記と同じ挙動)
# ①実行されるSQLをログに出力するかどうかを制御(これ単体ではログフォーマットには整形されない)
spring.jpa.show-sql=true
# ②SQLをログに出力する際に整形するかどうかを制御
#spring.jpa.properties.hibernate.format_sql=true
上記の①の記述により出力された例が下記です。findAllのURLにアクセスした場合の例です。確認用に有効にしておいても良いでしょう。
②のコメントアウトを外して有効にすると、下記の文が改行されます。

練習問題
問1: SQLで解いた練習問題(基本的なSQL文、SELECT文、SELECT文(集計関数、関数))を実装してください。
※「基本的なSQL文」の問1の「CREATE TABLE文で「輸出先」表を作成」の部分については除きます。
※テーブルはSQLの単元で作成したものを流用しても、新しいものを作成しても構いません。
※Repositoryには問ごとに1つ1つ実装し、Controller側ではマニュアルのsaveメソッドのように1つのControllerメソッド内に順にRepositoryメソッドを呼び出して結果を確認する流れでも構いません。(Repository側のメソッドと同数のControllerメソッドを作成しなくても良い。下記画像参照)

練習問題のヒント
問1 それぞれ以下の章を参考にすると良いでしょう
・基本的なSQL文 →「実践 – Repositoryの基本メソッド
・SELECT文 →「自動実装のクエリメソッド」
・SELECT文(集計関数、関数) →「@Queryアノテーションでのメソッド定義」
