第6章で作った Entity を使って、データベースを操作する Repository を作ります。この章では SQL を1行も書かずに、データベースからデータを取り出すところまで進みます。
この章で学ぶこと
- Repository インターフェースの作り方
- メソッド名から SQL が作られる仕組み(クエリメソッド)
- JUnit テストで Repository の動きを確かめる方法(任意)
7.1 Repository を作る
Entity ができたので、次はデータベースを操作する Repository(リポジトリ)を作ります。第5章で説明したとおり、Controller はこの Repository を使ってデータを取り出したり保存したりします。
repository パッケージを作る
- 起点となる
com.example.employeeappパッケージを右クリックし、新規 → パッケージ を選びます。 - 「名前」には、最初は右クリックした親のパッケージ名
com.example.employeeappが入っています。末尾に.repositoryを付け足してcom.example.employeeapp.repositoryにし、完了 をクリックします。

図7-1 最初は親のパッケージ名が入っている(上)ので、末尾に .repository を付け足す(下)
DepartmentRepository を作る
Repository は、クラスではなく インターフェース として作ります。
-
com.example.employeeapp.repositoryパッケージを右クリックし、新規 → インターフェース を選びます。

図7-2 repository パッケージを右クリック → 新規 → インターフェース
- 「パッケージ」に
com.example.employeeapp.repositoryが自動で入っていることを確認し、「名前」にDepartmentRepositoryと入力して 完了 をクリックします。

図7-3 パッケージを確認し、名前に DepartmentRepository と入力
- 次のコードを補完を使って入力し、保存します。
package com.example.employeeapp.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.employeeapp.entity.Department;
@Repository
public interface DepartmentRepository extends JpaRepository<Department, Integer> {
/** 全部署を部署IDの昇順で取得する */
List<Department> findAllByOrderByDeptIdAsc();
/** 部署IDで部署を1件取得する(見つからないときは null) */
Department findByDeptId(Integer deptId);
}

図7-4 入力した DepartmentRepository(赤枠は Spring Tools が表示する補助の文字)
【ご注意】灰色の「Show AOT-generated Implementation, Query, etc...」は入力しない
エディターでメソッドの上に表示される灰色の斜体の文字「Show AOT-generated Implementation, Query, etc...」は、Spring Tools(STS)が表示する補助の情報です。自分で入力するコードではありません。入力しなくてよく、表示されなくても問題ありません。
また、@Repository の行に黄色の電球や波線が表示されることがあります。これは「Spring Data の Repository には @Repository を付けなくても動く」という助言(警告)で、エラーではありません。この教材では付けたままにします(後の囲みも参照)。
EmployeeRepository を作る
同じ手順で、com.example.employeeapp.repository パッケージを右クリックして EmployeeRepository インターフェースを作ります。

図7-5 パッケージを確認し、名前に EmployeeRepository と入力
package com.example.employeeapp.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.employeeapp.entity.Employee;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
/** 全社員を社員IDの昇順で取得する */
List<Employee> findAllByOrderByEmpIdAsc();
/** 社員IDで社員を1件取得する(見つからないときは null) */
Employee findByEmpId(Integer empId);
}
ここまでで、パッケージ・エクスプローラーは次のようになっています。

図7-6 entity と repository のパッケージに、それぞれ2つずつファイルができた
ここまでで、プロジェクトの構成は次のようになっています。
com.example.employeeapp
├─ EmployeeAppApplication.java
├─ component
│ └─ GreetingMessage.java (第5章)
├─ controller
│ └─ HelloController.java (第4・5章)
├─ entity
│ ├─ Department.java ← 第6章で作成
│ └─ Employee.java ← 第6章で作成
└─ repository
├─ DepartmentRepository.java ← この章で作成
└─ EmployeeRepository.java ← この章で作成
中身を書かなくていいの?
Repository はインターフェースなので、メソッドの中身(処理)がありません。それでも動くのは、アプリの起動時に Spring Data JPA がこのインターフェースを実装したクラスを自動で作ってくれる からです。作られたオブジェクトは Bean として DI コンテナに登録されるので、第5章で学んだとおり、Controller のフィールドに @Autowired を付けて受け取れます。
JpaRepository を継承する
JpaRepository<Employee, Integer> の < > の中には、次の2つを指定します。
| 位置 | 指定するもの | EmployeeRepository の場合 |
|---|---|---|
| 1つ目 | 扱う Entity のクラス | Employee |
| 2つ目 | その Entity の主キー(@Id のフィールド)の型 |
Integer |
JpaRepository を継承するだけで、よく使う操作のメソッドが最初から使えるようになります。主なものは次のとおりです。この教材で使うのは save と deleteById です。
| メソッド | 働き | 使う章 |
|---|---|---|
save(entity) |
登録または更新する。主キーが null なら登録(INSERT)、値が入っていれば更新(UPDATE) |
第11・12章 |
deleteById(id) |
主キーを指定して削除する | 第9章 |
findAll() |
全件を取得する(並び順は決まっていない) | ― |
count() |
件数を取得する | ― |
【知識メモ】findById について
JpaRepository には主キーで1件取得する findById というメソッドもありますが、戻り値の扱いが少し難しいため、この教材では使いません。代わりに、自分で宣言した findByEmpId や findByDeptId を使います。これらは、見つかればその Entity を、見つからなければ null を返します。
【知識メモ】@Repository は付けなくても動く
Spring Data JPA の Repository は、@Repository を付けなくても Bean として登録されます。この教材では「データベースを操作する部品」であることがひと目でわかるように、あえて付けています。
7.2 メソッド名から SQL が作られる仕組み
findAllByOrderByEmpIdAsc や findByEmpId は、JpaRepository にはないメソッドです。それでも中身を書かずに動くのは、Spring Data JPA が メソッド名を読み取って、SQL を自動で組み立てる からです。このようなメソッドを クエリメソッド といいます。
基本のルール
| 書き方 | 意味 |
|---|---|
findBy + プロパティ名 |
そのプロパティ(フィールド)の値で検索する(WHERE 句になる) |
findAllBy … |
複数件を検索する(findBy と同じ意味で使える) |
OrderBy + プロパティ名 + Asc/Desc
|
並び順を指定する(ORDER BY 句になる)。Asc は昇順、Desc は降順 |
And/Or
|
条件をつなぐ |
GreaterThan/LessThan
|
より大きい/より小さい |
Containing |
部分一致(LIKE '%…%') |
【ご注意】プロパティ名は Entity のフィールド名
メソッド名に書くのは、テーブルの列名(EMP_ID)ではなく、Entity のフィールド名(empId)です。メソッド名の中では先頭を大文字にして EmpId と書きます。
メソッドと SQL の対応
次の表は、メソッド名からおおよそどんな SQL が作られるかを示したものです(実際に作られる SQL は、もう少し複雑な形になります)。
| メソッド | 作られる SQL のイメージ |
|---|---|
findAllByOrderByEmpIdAsc() |
SELECT * FROM EMPLOYEE ORDER BY EMP_ID ASC |
findByEmpId(empId) |
SELECT * FROM EMPLOYEE WHERE EMP_ID = ? |
findByName(name) |
SELECT * FROM EMPLOYEE WHERE NAME = ? |
findByAgeGreaterThan(age) |
SELECT * FROM EMPLOYEE WHERE AGE > ? |
findByGenderAndAddress(gender, address) |
SELECT * FROM EMPLOYEE WHERE GENDER = ? AND ADDRESS = ? |
findByNameContaining(name) |
SELECT * FROM EMPLOYEE WHERE NAME LIKE '%…%' |
findByDepartmentDeptId(deptId) |
SELECT * FROM EMPLOYEE WHERE DEPT_ID = ?( department フィールドの中の deptId で検索) |
findAllByOrderByDeptIdAsc()(DepartmentRepository) |
SELECT * FROM DEPARTMENT ORDER BY DEPT_ID ASC |
? の部分には、メソッドの引数で渡した値が入ります。この教材で実際に使うのは、findAllByOrderByEmpIdAsc・findByEmpId・findAllByOrderByDeptIdAsc・findByDeptId の4つです。それ以外は「こういう書き方もできる」という例です。
【知識メモ】戻り値の型で件数が決まる
複数件が返る可能性がある検索では、戻り値を List<Employee> にします。主キーでの検索のように必ず1件以下になる場合は、戻り値を Employee にできます。
7.3 任意:JUnit テストで Repository を呼び出す
この節は任意です。Repository が正しく動くかどうかは、第8章で画面を作れば確かめられます。初めて学ぶ人は、この節を飛ばして 7.5 まとめと確認問題 に進んでかまいません。あとで余裕ができたときに、戻って試してください。
まだ画面を作っていないので、Repository が正しく動くかどうかを JUnit テスト で確かめます。JUnit は Java のプログラムをテストするための仕組みで、Spring Boot のプロジェクトには最初から組み込まれています。
テストクラスを作る
- パッケージ・エクスプローラーで
src/test/javaを開き、その中の起点となるcom.example.employeeappパッケージを右クリックします。
(src/main/javaではないので注意してください) - 新規 → クラス を選びます。

図7-7 src/test/java の com.example.employeeapp を右クリック → 新規 → クラス
-
「ソース・フォルダー」が
employee-app/src/test/java、「パッケージ」がcom.example.employeeappになっていることを確認し、「名前」にRepositoryCheckTestと入力して 完了 をクリックします。

図7-8 ソース・フォルダーが src/test/java であることを確認して、名前に RepositoryCheckTest と入力 -
次のコードを補完を使って入力し、保存します。
package com.example.employeeapp;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.example.employeeapp.entity.Department;
import com.example.employeeapp.entity.Employee;
import com.example.employeeapp.repository.DepartmentRepository;
import com.example.employeeapp.repository.EmployeeRepository;
@SpringBootTest
class RepositoryCheckTest {
@Autowired
private EmployeeRepository employeeRepository;
@Autowired
private DepartmentRepository departmentRepository;
/** 全社員を取得できること */
@Test
void findAllEmployees() {
List<Employee> list = employeeRepository.findAllByOrderByEmpIdAsc();
for (Employee employee : list) {
System.out.println(employee.getEmpId() + " " + employee.getName() + " "
+ employee.getDepartment().getDeptName());
}
assertEquals(3, list.size());
}
/** 社員IDを指定して社員を取得できること */
@Test
void findEmployee() {
Employee employee = employeeRepository.findByEmpId(1);
assertEquals("雷禅 海皇", employee.getName());
}
/** 全部署を取得できること */
@Test
void findAllDepartments() {
List<Department> list = departmentRepository.findAllByOrderByDeptIdAsc();
assertEquals(3, list.size());
}
}
テストのコードの解説
| コード | 意味 |
|---|---|
@SpringBootTest |
テストを実行する前に Spring Boot のアプリを起動し、DI コンテナを用意する。これにより Repository が使えるようになる |
@Autowired |
DI コンテナから Bean を受け取ってフィールドに入れる |
@Test |
このメソッドがテストであることを表す。1つのメソッドが1つのテストになる |
assertEquals(期待値, 実際の値) |
2つの値が等しいかを確かめる。等しくなければテストは失敗になる |
テストを実行する
- パッケージ・エクスプローラーで
RepositoryCheckTest.javaを右クリックします。 - 実行 → JUnit テスト を選びます。

図7-9 RepositoryCheckTest.java を右クリック → 実行 → JUnit テスト
- Spring Boot が起動し、テストが実行されます。終わると「JUnit」ビューが表示されます。

図7-10 テストがすべて成功した(赤枠:JUnit ビューの緑のバーと「実行: 3/3 エラー: 0 失敗: 0」)
JUnit ビューのバーの色で、結果がわかります。
| バーの色 | 意味 |
|---|---|
| 緑 | すべてのテストが成功した。「実行: 3/3 エラー: 0 失敗: 0」と表示される |
| 赤 | 失敗したテストがある。失敗したメソッドをクリックすると、下の「障害トレース」に原因が表示される |
コンソールの出力を確認する
「コンソール」ビューを開くと、次のような出力が見つかります。まず、Hibernate が実行した SQL です(第4章で spring.jpa.show-sql=true を設定したので表示されます)。
Hibernate:
select
e1_0.emp_id,
e1_0.address,
e1_0.age,
e1_0.birth_date,
e1_0.dept_id,
e1_0.gender,
e1_0.name
from
employee e1_0
order by
e1_0.emp_id
Hibernate:
select
d1_0.dept_id,
d1_0.dept_name
from
department d1_0
where
d1_0.dept_id=?
メソッド名 findAllByOrderByEmpIdAsc から、order by … emp_id の付いた SELECT 文が作られていることがわかります。続いて部署テーブルへの SELECT も実行されています。これは、社員の department フィールドに部署の情報を詰めるために、JPA が自動で実行したものです。
さらに、テストの中で System.out.println した内容も表示されます。
1 雷禅 海皇 営業部
2 院照 心愛 総務部
3 林檎 星飛 開発部
社員の氏名と一緒に部署名も表示されていれば、@ManyToOne による部署の結び付けも正しく動いています。
【ご注意】テストの実行には Oracle が必要です
@SpringBootTest のテストは、実際にデータベースに接続します。Oracle Database が起動していないとテストは失敗します。なお、Eclipse でアプリ(第4章の Hello World など)を起動したままでも、テストは別に実行できます。
7.4 よくあるエラー
7.3 のテストが赤になったときや、第8章以降でアプリの起動に失敗したときは、JUnit ビューの「障害トレース」やコンソールに表示されたメッセージを確認し、次の表を参考に直してください。Entity の書き間違いによるエラーは、第6章 6.4 にまとめています。
| メッセージ(一部) | 主な原因 | 対処 |
|---|---|---|
No property 'xxx' found for type 'Employee' |
クエリメソッドの名前が Entity のフィールド名と合っていない | メソッド名のつづり(例:findByEmpId と フィールド empId)を確認する |
expected: <3> but was: <0> |
データが0件になっている | 第3章でサンプルデータを入れたあと COMMIT したかを確認する。COMMIT していないデータは、他の接続(アプリ)からは見えない |
Failed to load ApplicationContext |
Spring Boot の起動に失敗した | メッセージの下の方にある Caused by: の行を読むと、本当の原因(上の表や、第6章 6.4 の表のエラーなど)が書かれている |
【便利な使い方】エラーメッセージは下から読む
Java のエラーメッセージは長く表示されますが、本当の原因は最後の方の Caused by: に書かれていることが多いです。慌てずに、最後の Caused by: を探しましょう。
7.5 まとめと確認問題
まとめ
-
Repository は
JpaRepositoryを継承したインターフェースを書くだけで、実装は Spring Data JPA が自動で作る -
JpaRepositoryにはfindAll・findById・save・deleteByIdなどのメソッドが最初から用意されている -
findByEmpIdのような クエリメソッド は、メソッド名から SQL が自動で作られる - (任意)
@SpringBootTestを付けた JUnit テストで、画面がなくても Repository の動きを確かめられる
確認問題
問1 EmployeeRepository はインターフェースで、メソッドの中身を書いていません。それでも findByEmpId を呼び出せるのはなぜですか。
解答を見る
Spring Data JPA が、起動時に JpaRepository を継承したインターフェースを見つけ、メソッド名から SQL を組み立てる実装クラスを自動で作って Bean として登録するからです。私たちは、そのインターフェースを @Autowired で受け取って使います。
問2 EmployeeRepository に、住所(address)で社員を検索し、複数件を返すメソッドを追加するとしたら、どのように宣言しますか。
解答を見る
List<Employee> findByAddress(String address);
メソッド名の Address は、Entity のフィールド名 address の先頭を大文字にしたものです。複数件が返る可能性があるので、戻り値は List<Employee> にします。
問3 JpaRepository の save メソッドは、どのような場合に登録(INSERT)になり、どのような場合に更新(UPDATE)になりますか。
解答を見る
主キー(@Id のフィールド)が null の場合は登録(INSERT)、値が入っている場合は更新(UPDATE)になります。