1. test 1
  2. test 2
  3. test 3
  4. test 4
  5. test 5
  6. test 6
コース一覧
Spring Boot入門
ページングと並び替え

Spring Boot入門

Java で Web API サーバーを作るための Spring Boot の入門コースです。DI コンテナ、REST コントローラ、リクエスト検証、レイヤ分割、Spring Data JPA、例外ハンドリング、テストまでを扱います。毎レッスン、Spring が裏側でやっている判断そのものを pure Java の関数として自分で書き、フレームワークが何を肩代わりしてくれているのかを手触りで理解できる構成にしました。約 12 時間 (1 日 30 分 × 24 日) で 48 レッスンを修了できます。

1
Spring Bootとは何か
01. Spring Bootで何が作れるのか10分
02. サーブレットからフレームワークへ15分
03. ルーティング表という発想15分
04. プロジェクトの構成10分
05. 起動のしくみと自動設定15分
06. パスのマッチング15分
2
DIコンテナ
01. newで書くと何が困るのか15分
02. コンストラクタインジェクション15分
03. インターフェースへの依存15分
04. @Componentと@Bean15分
05. 自作のDIコンテナ20分
06. Beanのスコープとライフサイクル15分
3
RESTコントローラ
01. RESTという設計方針15分
02. @RestControllerの役割15分
03. @GetMappingとパス変数15分
04. クエリパラメータの受け取り15分
05. @PostMappingとリクエストボディ15分
06. ステータスコードの選び方15分
07. ResponseEntityで組み立てる15分
4
リクエストとレスポンスの変換
01. JSONとオブジェクトの往復15分
02. DTOとエンティティを分ける15分
03. recordでDTOを書く15分
04. 入力検証の基本15分
05. 検証エラーをまとめて返す15分
06. アノテーションによる検証15分
5
レイヤ構成とビジネスロジック
01. 3層アーキテクチャ10分
02. @Serviceに置くもの15分
03. 状態遷移をルールにする15分
04. 料金計算のような業務ロジック15分
05. @Transactionalとは何か15分
06. トランザクションの巻き戻し15分
6
データアクセス
01. @Entityとテーブルの対応15分
02. リポジトリという抽象15分
03. メソッド名からクエリを作る20分
04. 検索条件の組み立て15分
05. ページングと並び替え15分
06. N+1問題15分
7
例外処理と横断的関心事
01. 例外をHTTPに変換する15分
02. @ExceptionHandlerと@ControllerAdvice15分
03. エラーレスポンスの形を決める15分
04. フィルタとインターセプタ15分
05. ログとリクエストの追跡15分
06. 設定値の外部化15分
8
テストと仕上げ
01. テストの種類と使い分け15分
02. Serviceの単体テスト15分
03. モックで依存を差し替える15分
04. MockMvcでエンドポイントを試す15分
05. 本番へ出すための次の一歩10分

ページングと並び替え

このレッスンでは、ページ番号と1ページの件数から取り出す範囲を計算できるようになり、Pageable が裏でやっていることを説明できるようになります。

全件返してはいけません

一覧の API で findAll() をそのまま返すのは、データが少ないうちだけ許される書き方です。10万件になった瞬間に、データベースからの転送、Java のヒープ、JSON への変換、クライアント側の描画のすべてが同時に苦しくなります。区切って返す仕組みが要ります。

Spring Data JPA では Pageable を引数に足すだけで対応できます。

Java

public interface UserRepository extends JpaRepository<User, Long> { Page<User> findByAgeGreaterThan(int age, Pageable pageable); } Pageable pageable = PageRequest.of(0, 20, Sort.by("name").ascending()); Page<User> page = userRepository.findByAgeGreaterThan(18, pageable);

Page は取得した中身に加えて、総件数や総ページ数といった情報も持っています。getContent() で中身、getTotalElements() で総件数、getTotalPages() で総ページ数、hasNext() で次のページの有無が取れます。

ページ番号は0始まりです

最初に必ずつまずくのがここです。PageRequest.of(0, 20) が1ページ目、of(1, 20) が2ページ目を指します。画面には「1ページ目」と表示したいので、コントローラで受け取った値から1を引いて渡す、という変換をどこかに置くことになります。この変換を忘れると、利用者から見て2件目のページが最初に出る、という分かりにくい不具合になります。

SQL では OFFSET と LIMIT になります

ページングの正体は、ソート済みの結果から先頭を読み飛ばして必要な数だけ取る、という操作です。

SQL クエリ

SELECT * FROM users ORDER BY name ASC LIMIT 20 OFFSET 40;

読み飛ばす件数が OFFSET、取る件数が LIMIT です。ページ番号との関係は次の式になります。

値式
offsetページ番号 × 1ページ件数
limit1ページ件数
総ページ数総件数を1ページ件数で割った切り上げ

総ページ数の切り上げは、整数のまま (total + size - 1) / size と書くのが定番です。Math.ceil を使うと double が混ざり、件数が大きいときに誤差の心配が出てきます。

ORDER BY を必ず付けるという点も忘れないでください。並び順が決まっていない結果に対して OFFSET を掛けると、データベースは毎回同じ順序を返すとは限りません。1ページ目に出た行が2ページ目にも現れる、といった取りこぼしや重複が起きます。Sort を渡す、あるいはメソッド名に OrderBy を書くのは、見た目を整えるためではなく正しさのためです。

最後のページと範囲外

総件数が1ページ件数で割り切れないとき、最後のページは端数になります。総件数25件で1ページ10件なら、ページ0とページ1は10件ずつ、ページ2は5件です。総ページ数は3です。

さらに、存在しないページ番号を要求されることもあります。3ページしかないのにページ10を求められたら、返る件数は0件です。例外にせず空の結果を返すのが素直な扱いで、Spring の Page も同じ振る舞いをします。総件数が0件の場合、総ページ数は0とするのが Page の規約です。

演習で書くもの

演習では、Pageable から Page が組み立てられるまでの計算を自分で書きます。総件数、ページ番号、1ページの件数を受け取り、読み飛ばす件数、そのページに実際に載る件数、総ページ数の3つを求めて返します。端数の出る最終ページと、範囲外のページ番号がテストに含まれます。

要件

  1. 戻り値は offset=40,count=10,totalPages=5 の形にすること
  2. totalPages は切り上げで求め、total が 0 のときは 0 とすること
  3. count は最終ページの端数を正しく返し、範囲外のページでは 0 とすること

入出力例

page(100, 4, 10) → "offset=40,count=10,totalPages=10" page(25, 0, 10) → "offset=0,count=10,totalPages=3" page(25, 2, 10) → "offset=20,count=5,totalPages=3" page(25, 10, 10) → "offset=100,count=0,totalPages=3" page(0, 0, 20) → "offset=0,count=0,totalPages=0" page(7, 1, 7) → "offset=7,count=0,totalPages=1"

ヒント

切り上げは整数のまま (total + size - 1) / size で計算できます

offset は pageNumber * size です。範囲外かどうかは offset >= total で判定できます

count は残り件数 total - offset と size の小さい方です。Math.min が使えますが、残りが負になる場合を先に 0 に丸めてください

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/09

関連レッスン

  • N+1問題

    発行されるクエリ本数を数え、N+1 を検出できるようになります。

  • 例外をHTTPに変換する

    例外の種類からステータスコードを決める処理を実装します。

  • テストの種類と使い分け

    @SpringBootTest と @WebMvcTest の違いを確認します。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • エンドポイント「この URL を叩くとこの機能が動く」入口の住所
  • APIアプリ間でデータをやり取りする窓口
  • データベースデータを構造化して保存する仕組み
  • ヒープ親が子より常に大きい(最大ヒープ)または小さい(最小ヒープ)木構造。
  • applicationJSON 本文を送るときの Content-Type
  • 引数位置引数=順番で渡す。
  • SQLデータベースを操作するための共通言語
  • メソッドクラスに属する関数
main.java
学習モード
エディタを読み込んでいます

メモ

ページングと並び替え

⌘S で保存