AIによる要約
Spring BootのRepositoryでは、Optional、List、単一Entityなど戻り値の扱いでレビューされることがあります。大事なのは、0件、1件、複数件を想定し、get()で雑に取り出さないことです。検索条件不足もあわせて確認します。


Spring Data JPAを使うと、Repositoryのメソッドを簡単に作れます。findByIdやfindByEmailのようなメソッド名だけで検索できるため便利です。
一方で、戻り値の扱いを雑にすると、NoSuchElementException、想定外の500エラー、検索条件不足による誤更新につながります。
この記事のポイント
Optionalは存在しない可能性を表すget()で雑に取り出さないListは0件や複数件を前提に扱う- 1件だけのつもりなら、DB制約や検索条件も確認する
findByIdはOptionalで返る
Spring Data JPAのfindByIdは、値が存在しない可能性をOptionalで表します。IDを指定しても、削除済み、存在しないID、テストデータ不足などで0件になることがあります。
Optional<User> user = userRepository.findById(userId);
Optionalは、値があるかもしれないし、ないかもしれないという合図です。ここでいきなりget()するのは危険です。
User user = userRepository.findById(userId).get();
存在しないIDだった場合、例外になります。レビューでは、0件の場合にどうする仕様なのかを確認されます。
存在しないことが異常ならorElseThrowを使う
詳細画面や更新処理で、対象データが存在しないことが異常なら、明示的な例外に変換します。
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(userId));
この書き方なら、存在しない場合に何が起きるのかが読み手に伝わります。ControllerAdviceなどで例外をHTTP 404へ変換する設計にもつなげやすくなります。
存在しないことが正常なら分岐する
検索条件に該当しないことが正常な画面もあります。たとえば、任意のメールアドレスでユーザーを検索する画面では、0件はエラーではなく「見つかりません」かもしれません。
Optional<User> user = userRepository.findByEmail(email);
if (user.isEmpty()) {
return UserSearchResult.notFound();
}
return UserSearchResult.found(UserResponse.from(user.get()));
この場合は、存在しないことを業務上どう扱うかをServiceで表します。単に例外にするのではなく、仕様に合わせます。
Listは0件が普通にある
findByStatusやfindByCustomerIdのように複数件を返す検索では、戻り値はListになることが多いです。ここでは0件も正常な結果として扱います。
List<Order> orders = orderRepository.findByCustomerId(customerId);
if (orders.isEmpty()) {
return List.of();
}
return orders.stream()
.map(OrderResponse::from)
.toList();
0件を例外にするのか、空リストとして返すのかは仕様次第です。ただし、画面の一覧検索では空リストを返す方が自然なことが多いです。
1件だけのつもりなら検索条件を確認する
Repositoryメソッドで単一EntityやOptionalを返す場合、その検索条件で本当に1件に絞れるのか確認します。
Optional<User> findByEmail(String email);
メールアドレスがDB上でユニーク制約になっているなら、1件検索として自然です。しかし、DB制約がない場合、同じメールアドレスが複数件ある可能性があります。
CREATE UNIQUE INDEX uk_users_email ON users(email);
1件だけ取れる前提で実装するなら、DBのユニーク制約、業務仕様、検索条件を確認しましょう。
LIMIT 1でごまかさない
複数件返って困るからといって、安易にLIMIT 1で1件だけ取るのは危険です。どの1件が選ばれるかで業務結果が変わる可能性があります。
SELECT * FROM users WHERE email = 'test@example.com' LIMIT 1;
このSQLは、重複データがある問題を隠しているだけかもしれません。現場では、検索条件不足なのか、データ不整合なのか、仕様として複数件あり得るのかを確認します。
existsByやcountByを使う場面
データの中身が必要なく、存在チェックだけしたい場合は、existsByを使うと意図が明確になります。
boolean exists = userRepository.existsByEmail(email);
if (exists) {
throw new DuplicateEmailException(email);
}
件数が必要な場合はcountByを使います。ただし、件数を見てから登録する処理では、同時実行の問題もあるため、最終的にはDB制約も必要です。
現場レビューではどう見られるか
// レビューコメント例 // findByIdの戻り値に対してget()を直接呼んでいます。 // 対象データが存在しない場合の仕様が分からないため、 // orElseThrowで明示するか、0件時の扱いをServiceで分岐してください。
Repositoryの戻り値は、ただの型の話ではありません。0件、1件、複数件をどう扱うかという業務仕様の話につながります。
レビュー前のセルフチェック
Repository戻り値の確認リスト
Optional.get()を直接呼んでいないか- 0件が正常か異常か説明できるか
- 複数件あり得る検索なのに1件前提にしていないか
- 1件検索ならDB制約や業務キーを確認したか
- 存在チェックだけなら
existsByで十分ではないか
この記事とあわせて読みたい
まとめ
Repositoryの戻り値を見るときは、型だけでなく、0件、1件、複数件の業務上の意味を考えます。OptionalやListを丁寧に扱うだけで、現場で起きやすい検索ミスや想定外エラーを減らせます。
