Spring Data JPAでfindByNameと書くだけでクエリが動いた時、「魔法みたいだけど、何が起きているのかわからない」と感じたことはないでしょうか。私も最初はこの自動生成の仕組みが気持ち悪くて、あえて避けていた時期がありました。
結論から言うと、Spring Data JPAは(1)JpaRepositoryを継承するだけでCRUDが自動実装され、(2)メソッド名の命名規則だけで検索クエリが作れます。(3)複雑な集計やDB固有処理だけ@QueryやMyBatisに切り替える、という3段構えで使うのが実務の定石です。この記事では、この3段構えを実際のコードとN+1問題のような落とし穴も含めて解説します。
この記事でわかること
- JpaRepositoryによる基本のCRUD操作
- 命名規則によるクエリ自動生成の仕組み
- MyBatis・JdbcTemplateとの使い分け

最初はこの自動生成、正直ちょっと気持ち悪く感じました。
PARTS LIST /目次
Spring Data JPAとは?ORMとの関係
Spring Data JPAとは、JPA(Jakarta Persistence。旧称 Java Persistence API)というORMの仕様を、Springの世界でさらに簡単に使えるようにしたライブラリです。SQLをほとんど書かずに、Javaのメソッド呼び出しだけでデータベース操作ができるようになります。2025年11月にGAとなったSpring Boot 4系では、Jakarta EE 11・JPA 3.2に対応しています。Java 17以上が必須です(Spring Boot 4.1の公式ドキュメントでは Java 26 まで対応)。
JPA・Hibernate・Spring Data JPAの違いは?
JPA・Hibernate・Spring Data JPAは階層関係にあります。JPAは「Javaでどうデータベースを扱うか」を定めた仕様(インターフェース)です。Hibernateは、そのJPAという仕様を実際に実装したライブラリです。
そしてSpring Data JPAは、Hibernateなどの実装をさらに使いやすく包んだラッパーです。SQLを書く代わりにインターフェースを定義するだけで動く便利さがあります。この便利さは、3層構造の一番外側にあるSpring Data JPAが担っています。
UserRepository extends JpaRepositoryインターフェースを1つ書くだけ導入のメリット
最大のメリットは、単純なCRUD処理の実装コストがほぼゼロになることです。インターフェースを1つ定義するだけで、保存・検索・更新・削除の基本機能がすべて自動実装されます。複雑な検索条件も、後述する命名規則やJPQLで対応できるため、SQLを直接書く場面はかなり限定されます。Spring Bootのプロジェクトに組み込めば、依存関係を1つ追加するだけですぐに使い始められます。
JpaRepositoryで基本のCRUD操作を実装する
実装の第一歩は、JpaRepositoryインターフェースを継承するだけです。
依存関係の追加とRepositoryインターフェースの作成
spring-boot-starter-data-jpaを依存関係に追加したら、Entityクラスに対応するRepositoryインターフェースを作ります。
JPAアノテーションはjakarta.persistence.*パッケージに統一されています(javax.persistence.*からの切り替えはSpring Boot 3.0で行われました)。Spring Boot 2系までの情報を参照する場合はパッケージ名の読み替えが必要です。
public interface UserRepository extends JpaRepository<User, Long> {
}
たったこれだけで、実装クラスを1行も書かずに、後述する多数のメソッドが使えるようになります。JpaRepository<User, Long>の2つの型引数は、対象のEntityクラスと、その主キーの型を表しています。
save/findById/deleteByIdの基本メソッド
JpaRepositoryを継承した時点で、save()(登録・更新)、findById()(1件取得)、findAll()(全件取得)、deleteById()(削除)といった基本メソッドがすべて自動実装されます。
User user = new User("山田太郎");
userRepository.save(user);
Optional<User> found = userRepository.findById(1L);
userRepository.deleteById(1L);
findById()の戻り値はUserではなくOptional<User>です。「値があるかもしれない箱」なので、中身を取り出す前に「見つからなかった場合」をどうするか決めておく必要があります。orElse(null)で取り出して後ろの処理に渡してしまうと、結局どこかでNullPointerExceptionになります。
findById()の戻り値をOptionalのまま放置せず、orElseThrow()等で「見つからなかった場合」を明示的に分岐しておくと、NPEを未然に防げます。

『Good Code, Bad Code』を読んでから、「ない」をnullで返さず、Optionalではっきり伝えるようにしています。
ページネーション(Pageable)でデータを分割取得する
JpaRepositoryはfindAll(Pageable pageable)も自動実装しており、大量データをページ単位で分割取得できます。PageRequest.of(ページ番号, 件数)でページを指定するだけで、SQLのLIMIT/OFFSET相当の処理を意識せずに実装できます。
Pageable pageable = PageRequest.of(0, 20); // 1ページ目・20件ずつ
Page<User> page = userRepository.findAll(pageable);
page.getTotalElements(); // 総件数
page.getTotalPages(); // 総ページ数
ページネーションなしのfindAll()は、全件をメモリに読み込みます。一覧画面は最初からPageableを受け取る形にしておくと、データが増えてからの手戻りがありません。
メソッド名で自動生成する派生クエリの命名規則
Spring Data JPAの最大の特徴は、メソッド名を正しい命名規則で書くだけでクエリが自動生成される点です。
METHOD / メソッド名
findBy1Name2AndAge3OrderByIdDesc4(name, age)
↓ 頭から部品に分けて、クエリを組み立てる
QUERY / 相当するクエリ(イメージ)
PARTS / よく使うキーワード
ContainingfindByNameContainingLIKE '%…%'StartingWithfindByNameStartingWithLIKE '…%'GreaterThanfindByAgeGreaterThan> ?BetweenfindByAgeBetweenBETWEEN ? AND ?InfindByIdInIN (…)deleteBy は1回の DELETE ではなく、対象を読み込んでから1件ずつ消します。findBy/countBy/deleteByの基本パターン
findByに続けてフィールド名を書くだけで、そのフィールドを条件にした検索メソッドが作れます。countByは件数取得、deleteByは削除に対応します。
List<User> findByName(String name);
long countByAge(int age);
void deleteByEmail(String email);
複数条件はAnd/Orでつなぎ、findByNameAndAge(String name, int age)のように書けます。命名の規則さえ守れば、実装は一切不要です。ただし条件が4つ・5つと増えていくと、メソッド名がかえって読みにくくなります。
命名規則は万能ではなく、複雑になったら@Queryへの切り替えを検討する判断基準を持っておくとよいです。
複数条件・LIKE検索・DELETE文の書き方
ContainingやStartingWithを使えばLIKE検索ができます。findByNameContaining(String keyword)は部分一致検索になります。findByAgeGreaterThan(int age)のような比較演算子系のキーワードも用意されています。
見落としやすいのはコレクションを使った複数条件検索(findByIdIn(List<Long> ids))で、IN句相当のクエリになる点は覚えておくと役立ちます。
deleteByは1件削除だけでなく、条件に合致する全件を削除する使い方もできます。deleteByStatus(String status)のように書けば、指定したステータスのレコードをまとめて削除できます。戻り値の型をvoidではなくlongにすると、削除した件数を取得することも可能です。ただし内部では一括のDELETE文を1回発行するのではなく、該当するエンティティをSELECTで読み込んでから1件ずつ削除します(Spring Boot 4.1.1・Hibernate 7.4・H2で確認)。件数が多いとメモリと時間を使うため、大量削除には@Modifyingと@QueryでDELETE文を書く方法を検討してください。また、トランザクションの外から呼ぶと例外になるため、サービス層の@Transactionalなメソッドの中で呼びます。
メモ:命名規則の主なキーワード
Containing(部分一致)、StartingWith(前方一致)、GreaterThan(比較演算子)、In(IN句相当)などが代表的。
OrderBy・Betweenで並び替え・範囲検索する
OrderByをメソッド名に続けると、ソート順を指定した検索メソッドが作れます。findByAgeOrderByNameAsc(int age)のように、フィールド名の後にAsc(昇順)・Desc(降順)を付けます。OrderByAgeDescNameAscのように複数フィールドを連結して指定することも可能です。
範囲検索にはBetweenが使えます。findByAgeBetween(int start, int end)と書けば、SQLのBETWEEN句に相当する範囲指定検索になります。
ページネーション画面でソート条件も一緒に扱いたい場合は、メソッド名にOrderByを書き込むより、Pageable側にSortを含めて渡す方法がおすすめです。呼び出し側で条件を柔軟に変更でき、可読性も高くなります。
@QueryでJPQL・ネイティブSQLを書く方法
命名規則だけでは表現しきれない複雑なクエリは、@Queryアノテーションで直接書けます。
JPQLの基本構文
JPQLはSQLに似ていますが、テーブル名ではなくEntityクラス名、カラム名ではなくフィールド名を使う点が異なります。
@Query("SELECT u FROM User u WHERE u.age >= :minAge")
List<User> findAdults(@Param("minAge") int minAge);
GROUP BYなど集計クエリをJPQLで書く例
GROUP BYのような集計クエリは、メソッド名の命名規則だけでは表現できません。こうした集計処理は@QueryでJPQLを直接書く必要があります。
@Query("SELECT u.department, COUNT(u) FROM User u GROUP BY u.department")
List<Object[]> countByDepartment();
ネイティブSQLを使うべき場面
DB固有の関数を使いたい、複雑な結合やサブクエリでJPQLでは表現しづらい場合は、@Query(nativeQuery = true)でSQLをそのまま書けます。
ただしDB移植性が失われるため、本当に必要な箇所だけに絞るのが無難です。
MyBatis・JdbcTemplateとの使い分け
Spring Data JPAは万能ではありません。プロジェクトの性質によっては、他の選択肢のほうが適しています。
それぞれの得意分野
Spring Data JPAは、単純なCRUDが多いアプリケーションで開発速度を大きく上げられます。MyBatisは、SQLを直接書きたい、複雑な集計クエリが多いプロジェクトに向いています。JdbcTemplateは、その中間で、少量のSQLを軽量に実行したい場面に適しています。
ただしSpring Data JPAには注意点もあります。リレーションを持つEntityでは、意図せず大量のクエリが発行される「N+1問題」が起きやすいです。パフォーマンスチューニングの難易度は、MyBatisより高くなりがちです。

N+1問題は最初ハマりやすいので要注意です。
@Transactionalが効かない落とし穴
Spring Data JPAのRepositoryをサービス層から呼び出す構成では、@Transactionalの付け方を誤ると実務でよく事故が起きます。同一クラス内の別メソッドを呼び出す形で@Transactionalを使っても、SpringのAOPプロキシを経由しないため、呼ばれた側に付けた@Transactionalは効きません。
userService.register()register()@Transactional なしプロキシを通らない
saveDetail()@Transactional 付き✕ saveDetail() の @Transactional は効かない(まとめて1つのトランザクションにならず、Repository の呼び出しごとに別々のトランザクションで動く)
厄介なのは、多くの場合エラーにならずにそのまま動いてしまうことです。テストでは気づかず、本番でデータの不整合として表に出てから原因を探すことになりがちです。トランザクションをかけたい処理は別のBeanに切り出し、サービス層の境界を意識して分けておきましょう。
実務ではどちらを選ぶべき?
まずSpring Data JPAで作り始めるのが実務では定番です。パフォーマンスや複雑なクエリを要求される場面では、JPQLやネイティブSQLでは対応しきれません。そうした箇所だけ、部分的にMyBatisやJdbcTemplateを併用する構成がよく見られます。
最初から全てをMyBatisで書くより、開発速度と柔軟性のバランスが取りやすい選び方です。個人的には、@QueryのJPQLで2〜3行を超えるほど複雑になった時点が、MyBatisへの切り替えを検討する目安だと考えています。
よくある質問
Q. Spring Data JPAは初心者でも使えますか?
A. はい。JpaRepositoryを継承するだけで基本のCRUD操作が使えるため、SQLの知識が浅くても始められます。ただし裏側の仕組み(JPA・Hibernate)を理解しておくと、トラブル時の対処がしやすくなります。
Q. 命名規則のメソッド名が長くなりすぎたらどうすればいいですか?
A. @QueryでJPQLを直接書くほうが可読性は高くなります。条件が3つ以上になったら@Queryへの切り替えを検討するとよいです。
Q. Spring Data JPAとHibernateの違いは何ですか?
A. HibernateはJPA仕様を実装したORM本体で、Spring Data JPAはそのHibernateなどの実装をさらに簡単に使うためのラッパーライブラリです。
Q. MyBatisとどちらを使うべきですか?
A. 単純なCRUDが中心ならSpring Data JPA、複雑な集計SQLやDB固有機能を多用するならMyBatisが向いています。両者を併用するプロジェクトも珍しくありません。
Q. N+1問題とは何ですか?
A. リレーションを持つEntityを取得する際、意図せず1件ずつ追加のクエリが発行されてしまう現象です。Spring Data JPAで起きやすく、パフォーマンス低下の原因になります。
Q. ページネーションはどう実装しますか?
A. JpaRepositoryが自動実装するfindAll(Pageable pageable)を使います。PageRequest.of(ページ番号, 件数)でページを指定するだけで、大量データを分割取得できます。
まとめ|Spring Data JPAで最短実装するCRUD
Spring Data JPAのポイントは、次の3点です。
JpaRepositoryを継承するだけで基本のCRUD操作が自動実装される- メソッド名の命名規則でシンプルな検索クエリを自動生成できる
- 複雑なクエリは
@QueryのJPQL・ネイティブSQLで、必要ならMyBatis・JdbcTemplateも併用する
最初は自動生成の仕組みが気持ち悪く感じるかもしれませんが、命名規則さえ覚えてしまえば、SQLを1行も書かずに実装が進む快適さに気づくはずです。まずはJpaRepositoryを継承した小さなRepositoryを1つ作って、実際に動かしてみてください。
| REV. | DATE | 内容 |
|---|---|---|
| 4 | 2026-10-15 | 補足・注意の囲みを整理し、Optionalと@Transactionalの説明を書き直し |
| 3 | 2026-10-04 | 記載内容の事実確認と修正 |
| 2 | 2026-09-22 | 2026年9月時点の情報で全体を見直し(古い記述を2件更新) |
| 1 | 2026-09-19 | 初版 |

