CrudRepositoryとJpaRepositoryの違いを完全ガイド!初心者でもわかるリポジトリの基本
新人
「先輩、Spring Data JPAでCrudRepositoryとJpaRepositoryってよく見かけるんですが、どう違うんですか?」
先輩
「それは良い質問だ!どちらもデータベース操作を簡単にするインターフェースだけど、機能や使いどころが違うんだ。基本から一緒に見ていこう!」
新人
「お願いします!実際の使い方も知りたいです!」
先輩
「もちろんだ!それでは、まずはCrudRepositoryから説明しよう。」
1. CrudRepositoryとは?
CrudRepositoryは、Spring Data JPAで提供されるインターフェースで、基本的なCRUD操作(Create、Read、Update、Delete)を簡単に実装できます。これにより、SQLを自分で書かなくてもデータ操作が可能になります。
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends CrudRepository<Product, Long> {
}
上記のコードで、ProductRepositoryがCrudRepositoryを継承することで、以下のメソッドが自動的に使用できます。
save(S entity): 新しいエンティティの保存や既存エンティティの更新findById(ID id): IDを基にデータ取得findAll(): 全データの取得deleteById(ID id): IDを基にデータ削除
2. JpaRepositoryとは?
JpaRepositoryは、CrudRepositoryを拡張したインターフェースで、さらに多くの便利な機能を提供します。特に、ページネーションやソート、バッチ処理など、実用的な機能が充実しています。
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}
JpaRepositoryを使用することで、CrudRepositoryの機能に加え、以下のメソッドが利用可能です。
findAll(Pageable pageable): ページネーションを伴うデータ取得findAll(Sort sort): ソート付きデータ取得saveAll(Iterable<S> entities): 複数エンティティの一括保存flush(): 永続化コンテキストの即時反映
以下はページネーションの例です。
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
Page<Product> products = productRepository.findAll(PageRequest.of(0, 5));
このコードでは、最初の5件のデータを取得しています。これにより、大量データを扱う際に効率的な処理が可能です。
3. CrudRepositoryの基本機能と使用例
CrudRepositoryは、Spring Data JPAで提供されるインターフェースで、基本的なCRUD操作(Create、Read、Update、Delete)を簡単に実装できます。SQLを書かずにデータベース操作ができるため、初心者でも扱いやすいのが特徴です。
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends CrudRepository<Product, Long> {
}
上記のProductRepositoryでは、以下のような標準メソッドが利用可能です。
save(S entity): エンティティの新規保存や更新findById(ID id): 指定したIDのデータを取得findAll(): 全データを取得deleteById(ID id): 指定したIDのデータを削除
例えば、商品情報を保存するコードは以下の通りです。
@Autowired
private ProductRepository productRepository;
public void addProduct() {
Product product = new Product();
product.setName("ノートパソコン");
product.setPrice(120000);
productRepository.save(product); // 商品を保存
}
4. JpaRepositoryの基本機能と使用例
JpaRepositoryは、CrudRepositoryを拡張したインターフェースで、ページネーションやソート、バッチ処理など、実務で便利な機能が追加されています。
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}
JpaRepositoryでは、以下のメソッドが利用できます。
findAll(Pageable pageable): ページ単位でデータ取得findAll(Sort sort): 指定条件でソートしてデータ取得saveAll(Iterable<S> entities): エンティティの一括保存flush(): 永続化コンテキストを即座にデータベースへ反映
ページネーションの使用例は以下の通りです。
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
public void getPagedProducts() {
Page<Product> products = productRepository.findAll(PageRequest.of(0, 5)); // 最初の5件を取得
products.forEach(product -> System.out.println(product.getName()));
}
5. CrudRepositoryとJpaRepositoryの共通点と相違点
CrudRepositoryとJpaRepositoryは、どちらもデータベース操作を簡単に行えるインターフェースですが、使用できる機能や用途が異なります。以下に主な共通点と相違点をまとめます。
5.1 共通点
- 基本的なCRUD操作(保存、取得、更新、削除)が可能
- SQLを書かずにデータベース操作ができる
- Spring Data JPAの恩恵で、簡単にリポジトリを作成できる
5.2 相違点
| 項目 | CrudRepository | JpaRepository |
|---|---|---|
| 基本機能 | CRUD操作のみ | CRUD操作 + ページネーション、ソート、バッチ処理 |
| 推奨使用場面 | 簡単なデータ処理が必要な場合 | 大量データ処理や複雑な操作が必要な場合 |
| コード記述量 | 少ない | やや多いが、機能は豊富 |
| 柔軟性 | 限定的 | 柔軟かつ拡張性が高い |
5.3 どちらを使うべきか?
単純なデータ登録・取得であればCrudRepositoryで十分ですが、実務ではJpaRepositoryを使用することが一般的です。理由は、ページネーションやソートなどが実装しやすく、後々の機能拡張にも対応しやすいからです。
実践的なコード比較
両者の実装例を比較してみましょう。
CrudRepositoryを使用したデータ取得
public void getAllProducts() {
Iterable<Product> products = productRepository.findAll();
products.forEach(product -> System.out.println(product.getName()));
}
JpaRepositoryを使用したソート付きデータ取得
import org.springframework.data.domain.Sort;
public void getSortedProducts() {
List<Product> products = productRepository.findAll(Sort.by("price").descending());
products.forEach(product -> System.out.println(product.getName() + ": " + product.getPrice()));
}
このように、JpaRepositoryでは少ないコードで高度な操作が可能になります。
6. どちらを選ぶべきか?使用シーンの比較
CrudRepositoryとJpaRepositoryの選択は、プロジェクトの規模や要件に大きく影響します。このセクションでは、それぞれの使用シーンと選び方について詳しく説明します。
6.1 CrudRepositoryを選ぶべき場合
- 単純なCRUD操作が中心で、複雑なクエリが不要な場合
- 小規模なアプリケーションやプロトタイプの開発時
- パフォーマンスや拡張性よりも、シンプルさを重視する場合
public void saveSimpleProduct() {
Product product = new Product();
product.setName("マウス");
product.setPrice(2500);
productRepository.save(product); // 簡単な保存操作
}
6.2 JpaRepositoryを選ぶべき場合
- 大量のデータ処理が必要な場合
- ページネーションやソート機能を簡単に実装したい場合
- 将来的な機能拡張を見越している場合
- バッチ処理や一括保存が頻繁に求められるシナリオ
import org.springframework.data.domain.PageRequest;
public void getProductsWithPagination() {
productRepository.findAll(PageRequest.of(1, 10)) // 2ページ目の10件を取得
.forEach(product -> System.out.println(product.getName()));
}
7. よくあるエラーとその解決方法
CrudRepositoryやJpaRepositoryを使う際、初心者がつまずきやすいエラーとその解決方法を紹介します。
7.1 "EntityNotFoundException"が発生する場合
原因: データベースに存在しないIDを検索した場合に発生します。
Optional<Product> product = productRepository.findById(100L);
if (product.isEmpty()) {
System.out.println("指定したIDの商品は存在しません。");
}
対処法: Optionalを活用し、データが存在するか確認してから処理を実行しましょう。
7.2 "TransactionRequiredException"の対処法
原因: データの保存や削除をトランザクション外で実行した場合に発生します。
import org.springframework.transaction.annotation.Transactional;
@Transactional
public void deleteProduct(Long id) {
productRepository.deleteById(id); // トランザクション内で実行が必要
}
対処法: @Transactionalを付与してメソッドを実行してください。
7.3 "NoSuchBeanDefinitionException"の解決方法
原因: リポジトリインターフェースに@Repositoryアノテーションがない、またはSpring Bootのコンポーネントスキャン範囲外にある場合に発生します。
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> { }
対処法: 必ず@Repositoryを付け、適切なパッケージに配置してください。
8. 実践アドバイス
この記事では、CrudRepositoryとJpaRepositoryの違いと使い分けについて解説しました。簡単なCRUD操作だけであればCrudRepositoryで十分ですが、実務レベルの開発では、柔軟性や機能面で優れたJpaRepositoryの使用が推奨されます。
- シンプルな処理:
CrudRepositoryが最適 - ページネーションやソートが必要:
JpaRepositoryを選択 - エラー対応:
Optionalや@Transactionalを活用し、安定したコードを心がけましょう
実践アドバイス
- 最初は
CrudRepositoryで始め、必要に応じてJpaRepositoryへ移行すると無駄がありません。 - 複雑なクエリが必要になったら、
@Queryアノテーションやカスタムクエリを導入しましょう。 - テストコードを併用し、リポジトリの動作確認を習慣にしてください。
今後、Spring Data JPAを用いたアプリケーション開発では、これらの知識が確実に役立ちます。ぜひ実際のプロジェクトで試してみてください!
まとめ
ここまでの記事では、Spring Data JPA における CrudRepository と JpaRepository の違いと共通点、リポジトリインターフェースを使った基本的なデータベース操作の考え方を順を追って確認してきました。あらためて振り返ると、どちらのリポジトリもデータの作成や更新、削除や検索といった基本的な処理をとても少ないコードで記述できるという大きな利点を持っています。その一方で、扱う場面やアプリケーションの規模によって向き不向きがあり、どちらを選ぶのかを意識して設計することが大切だということも見えてきました。特に、画面数が増え、商品一覧やユーザー一覧のように大量のデータをページ単位で扱う場面が増えてくると、JpaRepository が提供するページネーションやソートの機能が心強い味方になります。 初心者の段階では、CrudRepository のようにシンプルなインターフェースから入ると理解しやすく、まずは「保存してみる」「ひとつ取り出してみる」「全部取得してみる」「削除してみる」といった素直な操作の流れに慣れることができます。エンティティクラスに名前や価格、メールアドレスなどのフィールドを定義し、リポジトリを通してそれらをデータベースのテーブルと関連付ける経験を積むことで、オブジェクトとテーブルの関係が少しずつ体感としてつかめるようになります。小さなサンプルから始めて、テーブルのレコードが画面の一覧と結びついて見えるようになってくると、Spring Data JPA を使った開発の楽しさが実感できるはずです。 一方で、実際の業務システムでは、ただ保存できればよいというわけではなく、一覧画面での絞り込み、並び替え、ページ切り替え、履歴表示など、現場ならではの要望が次々と出てきます。そのときに選択肢として浮かんでくるのが JpaRepository です。JpaRepository は CrudRepository を拡張したインターフェースであり、CrudRepository が得意とする基本的な CRUD 処理に加えて、ページングやソート、一括保存、永続化コンテキストの同期といったより実践的な機能を持っています。最初から JpaRepository を採用しておけば、後から要件が増えた場合にも同じリポジトリを拡張して対応しやすく、長期的な運用を見据えたときにも安心感があります。 また、どちらのリポジトリを選んだとしても、土台となるエンティティの設計やアノテーションの付け方は共通です。クラスに@Entity を付け、主キーに@Id を設定し、必要に応じて@Column で必須項目や一意制約を指定しておくことで、データの整合性が保たれます。この記事の中では Product や User といった分かりやすい例を使いましたが、実際の現場では注文情報や在庫情報、ログや履歴など多様なエンティティが登場します。それぞれのエンティティに対して対応するリポジトリを用意し、責務を分けておくことで、アプリケーション全体の見通しが良くなり、あとから仕様を追加するときも迷いにくくなります。
CrudRepository と JpaRepository を意識した設計のイメージ
最後に、簡単なサンプルとして Product エンティティを使ったリポジトリ定義とコントローラのイメージをあらためて確認しておきます。実際に自分の手元のプロジェクトで少し書き換えて試すことで、違いがよりはっきりと感じられるでしょう。
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
// 名前で部分一致検索するメソッドを追加することもできます
List<Product> findByNameContaining(String name);
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ProductController {
@Autowired
private ProductRepository productRepository;
@GetMapping("/products/page")
public String listProducts(Model model) {
Page<Product> page = productRepository.findAll(PageRequest.of(0, 10));
model.addAttribute("page", page);
model.addAttribute("products", page.getContent());
return "productList";
}
}
上のサンプルでは、JpaRepository を利用してページ単位で商品一覧を取得し、テンプレートエンジンに渡す流れを表現しています。もし CrudRepository を使って同じことをしようとすると、自分でページング用のクエリや件数計算の処理を用意する必要があり、コード量も複雑さも増えてしまいます。もちろん、学習段階であればあえて自前実装に挑戦してみるのも良い経験ですが、実務の現場では限られた時間の中で確実に動く仕組みを整えることが重要になるため、標準で用意されている機能をうまく借りる発想が欠かせません。 CrudRepository と JpaRepository の違いを理解しておくことは、単にメソッドの数を覚えるということではなく、「いま作ろうとしている画面や機能がどの程度の規模で、これから先どんな拡張が考えられるのか」を自然と意識できるようになるきっかけにもなります。小さく始めて大きく育てるのか、最初からある程度先を見据えておくのか、その判断を助けてくれるのがリポジトリの選び方だと言えるでしょう。
これから本格的に Spring を使った開発に取り組む人にとって、CrudRepository と JpaRepository を一度じっくり比較しておくことは、土台づくりの意味でも大きな価値があります。どのクラスにどの責務を持たせるのか、どの層でリポジトリを呼び出すのか、例外が発生したときにどこで扱うのかといった設計の考え方は、言語やフレームワークが変わっても応用できます。今回学んだことをきっかけに、サービス層やコントローラ層との役割分担、テストコードの書き方、将来的なリファクタリングのしやすさなどにも目を向けていくと、コード全体の質が少しずつ上がっていきます。ひとつひとつのリポジトリと丁寧に向き合うことで、データベースとアプリケーションの橋渡しをするという役割の大切さが、自然と実感できるようになるでしょう。
生徒:「CrudRepository と JpaRepository の違いがようやくつながってきました。どちらも同じように見えていたんですけど、機能の幅や使う場面が違うんですね。」
先生:「その感覚が持てれば十分だよ。最初のうちはどちらを使っても動いてしまうから違いが分かりにくいけれど、ページングやソートが必要になった瞬間に JpaRepository の便利さがぐっと際立ってくるんだ。」
生徒:「確かに、一覧画面を考えるとページごとに区切って表示したくなる場面が多いので、最初から JpaRepository を選んでおくと安心だと感じました。」
先生:「そうだね。ただ、小さな検証用のプロジェクトや、とにかく動くものを素早く作りたいときには、CrudRepository のような素直な書き方も悪くない。大切なのは、どちらの選択肢も知ったうえで意識的に選べることなんだ。」
生徒:「エンティティの設計やアノテーションも、どちらのインターフェースを使う場合でも共通して大事だと分かりました。土台がしっかりしていないと、どちらを使っても期待どおりに動いてくれないんですね。」
先生:「その通り。エンティティやテーブルの関係が整理されていると、リポジトリのコードは驚くほど短くて済む。今日学んだ内容を踏まえて、自分のプロジェクトでも小さなリポジトリから試してみるといいよ。」
生徒:「はい。まずは簡単な商品管理の画面を作って、CrudRepository 版と JpaRepository 版を両方試してみようと思います。違いを自分の目で確かめてみたいです。」
先生:「それは良い勉強になるね。同じ機能を別のアプローチで実装してみると、フレームワークの設計思想がよく見えてくるから、ぜひ挑戦してみて。」