0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Spring入門】第7章 JPA 入門② Repository

0
Last updated at Posted at 2026-10-11

第6章で作った Entity を使って、データベースを操作する Repository を作ります。この章では SQL を1行も書かずに、データベースからデータを取り出すところまで進みます。

この章で学ぶこと

  • Repository インターフェースの作り方
  • メソッド名から SQL が作られる仕組み(クエリメソッド)
  • JUnit テストで Repository の動きを確かめる方法(任意)

7.1 Repository を作る

Entity ができたので、次はデータベースを操作する Repository(リポジトリ)を作ります。第5章で説明したとおり、Controller はこの Repository を使ってデータを取り出したり保存したりします。

repository パッケージを作る

  1. 起点となる com.example.employeeapp パッケージを右クリックし、新規 → パッケージ を選びます。
  2. 「名前」には、最初は右クリックした親のパッケージ名 com.example.employeeapp が入っています。末尾に .repository を付け足して com.example.employeeapp.repository にし、完了 をクリックします。

new-package-repository.png
図7-1 最初は親のパッケージ名が入っている(上)ので、末尾に .repository を付け足す(下)

DepartmentRepository を作る

Repository は、クラスではなく インターフェース として作ります。

  1. com.example.employeeapp.repository パッケージを右クリックし、新規 → インターフェース を選びます。

new-interface-menu.png
図7-2 repository パッケージを右クリック → 新規 → インターフェース

  1. 「パッケージ」に com.example.employeeapp.repository が自動で入っていることを確認し、「名前」に DepartmentRepository と入力して 完了 をクリックします。

new-interface-department.png
図7-3 パッケージを確認し、名前に DepartmentRepository と入力

  1. 次のコードを補完を使って入力し、保存します。
src/main/java/com/example/employeeapp/repository/DepartmentRepository.java
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);

}

department-repository.png
図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 インターフェースを作ります。

new-interface-employee.png
図7-5 パッケージを確認し、名前に EmployeeRepository と入力

src/main/java/com/example/employeeapp/repository/EmployeeRepository.java
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);

}

ここまでで、パッケージ・エクスプローラーは次のようになっています。

explorer.png
図7-6 entity と repository のパッケージに、それぞれ2つずつファイルができた

ここまでで、プロジェクトの構成は次のようになっています。

パッケージ・エクスプローラー(src/main/java)
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 のプロジェクトには最初から組み込まれています。

テストクラスを作る

  1. パッケージ・エクスプローラーで src/test/java を開き、その中の起点となる com.example.employeeapp パッケージを右クリックします。
    (src/main/java ではないので注意してください)
  2. 新規 → クラス を選びます。

new-test-class-menu.png
図7-7 src/test/java の com.example.employeeapp を右クリック → 新規 → クラス

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

  2. 次のコードを補完を使って入力し、保存します。

src/test/java/com/example/employeeapp/RepositoryCheckTest.java
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つの値が等しいかを確かめる。等しくなければテストは失敗になる

テストを実行する

  1. パッケージ・エクスプローラーで RepositoryCheckTest.java を右クリックします。
  2. 実行 → JUnit テスト を選びます。

run-junit-menu.png
図7-9 RepositoryCheckTest.java を右クリック → 実行 → JUnit テスト

  1. Spring Boot が起動し、テストが実行されます。終わると「JUnit」ビューが表示されます。

junit-result.png
図7-10 テストがすべて成功した(赤枠:JUnit ビューの緑のバーと「実行: 3/3 エラー: 0 失敗: 0」)

JUnit ビューのバーの色で、結果がわかります。

バーの色 意味
緑 すべてのテストが成功した。「実行: 3/3 エラー: 0 失敗: 0」と表示される
赤 失敗したテストがある。失敗したメソッドをクリックすると、下の「障害トレース」に原因が表示される

コンソールの出力を確認する

「コンソール」ビューを開くと、次のような出力が見つかります。まず、Hibernate が実行した SQL です(第4章で spring.jpa.show-sql=true を設定したので表示されます)。

コンソール(SQL の出力例。実際の表示とは細部が異なります)
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 した内容も表示されます。

コンソール(System.out の出力)
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)になります。


前へ

第6章 JPA 入門① Entity

次へ

第8章 社員一覧画面

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?