# 業務ロジック — OwnerController.addPaginationModel() [8 LOC]

| 項目 | 値 |
|-------|-------|
| 完全修飾名 | `org.springframework.samples.petclinic.owner.OwnerController` |
| レイヤー | Controller |
| モジュール | `owner` (パッケージ: `org.springframework.samples.petclinic.owner`)

## 1. 役割

### OwnerController.addPaginationModel()

`addPaginationModel` メソッドは、ペットクリニック管理アプリケーションにおける「オーナー（飼い主）検索結果のページネーション表示」準備を担当する、View 向けのモデル構築ユーティリティメソッドです。

このメソッドは、`processFindForm` メソッド（最終的な検索結果が複数のオーナーに該当した場合の分岐）から呼び出され、ページネーション処理済みのオーナー一覧データ `Page<Owner>` を受け取り、Spring MVC の `Model` オブジェクトにビュー描画に必要な全データを格納します。具体的には、現在のページ番号 (`currentPage`)、全体のページ数 (`totalPages`)、全体の件数 (`totalItems`)、そして実際のオーナー一覧 (`listOwners`) の 4 つの属性をモデルに追加します。

設計パターンとしては **Data Binder（データ結合パターン）** を実装しており、ビジネスロジック層（`findByLastNameStartingWith` を通じての `OwnerRepository`）から取得済みのページネーション結果を、View レイヤ（Thymeleaf テンプレート `owners/ownersList`）がそのまま利用できるよう整形してバインドします。このメソッドは画面に依存しない汎用的な変換処理であり、オーナー検索結果をリスト表示する唯一のエントリポイントとして機能します。

条件分岐は存在せず、単一の正規ルートでモデル構築を行い、`owners/ownersList` ビュー名を返すことで、呼び出し元の `processFindForm` に表示制御を委譲します。

## 2. 処理パターン（詳細業務ロジック）

```mermaid
flowchart TD
    START(["addPaginationModel ページ番号, モデル, ページネーション結果"])

    START --> EXTRACT["List<Owner> listOwners = paginated.getContent()"]
    EXTRACT --> ADD1["model.addAttribute - currentPage, page"]
    ADD1 --> ADD2["model.addAttribute - totalPages, paginated.getTotalPages()"]
    ADD2 --> ADD3["model.addAttribute - totalItems, paginated.getTotalElements()"]
    ADD3 --> ADD4["model.addAttribute - listOwners, listOwners"]
    ADD4 --> RETURN(["return owners/ownersList"])
```

本メソッドには条件分岐（if / switch）が存在しないため、処理フローは直線的です。各ステップの詳細は以下の通りです。

1. **ページネーション結果からオーナー一覧の抽出** — `Page<Owner>` オブジェクトから現在のページに属する `Owner` エンティティのリスト (`listOwners`) を取り出します。
2. **モデルへの属性追加（ページ番号）** — 現在のページ番号（1 起点）を `currentPage` 属性として Spring MVC `Model` に格納し、ページネーションUI（「前へ」「次へ」ボタンの状態）で利用可能にします。
3. **モデルへの属性追加（総ページ数）** — `getTotalPages()` から全体のページ数を取得し、`totalPages` 属性として格納します。これにより、ページネーションバーの総ページ数が決定されます。
4. **モデルへの属性追加（総件数）** — `getTotalElements()` から検索結果の総件数を取得し、`totalItems` 属性として格納します。これにより、一覧上部に「合計 N 件」といった件数表示が行えます。
5. **モデルへの属性追加（オーナー一覧）** — 抽出済みの `listOwners` を `listOwners` 属性として `Model` に格納し、Thymeleaf テンプレートでリスト描画できるよう準備します。
6. **ビュー名の返却** — 表示テンプレート名 `"owners/ownersList"` を返値とし、`processFindForm` に制御を戻します。

## 3. パラメータ分析

| No | パラメータ名 | 型 | 業務的説明 |
|----|---------------|------|----------------|
| 1 | `page` | `int` | ページネーションにおける現在のページ番号（1 起点）。`processFindForm` の `@RequestParam(defaultValue = "1")` から渡され、URLパラメータ `?page=N` によって操作可能。この値はビュー上のページネーションUIで「現在地」を強調表示するために使用される |
| 2 | `model` | `Model` | Spring MVC のモデルオブジェクト。View 側に渡すデータを格納するコンテナ。本メソッドはページ番号・総ページ数・総件数・オーナー一覧の 4 属性をここに追加し、Thymeleaf テンプレート (`owners/ownersList`) がこれらを読み取る |
| 3 | `paginated` | `Page<Owner>` | 検索結果のページネーションラッパー。`findPaginatedForOwnersLastName` によって `OwnerRepository.findByLastNameStartingWith` を通じて取得されたデータを含む。`getContent()` でオーナー一覧、`getTotalPages()` で総ページ数、`getTotalElements()` で総件数を取得可能 |

**外部状態・インスタンスフィールド:**

| 種別 | フィールド名 | 業務的説明 |
|------|---------------|----------------|
| Instance Field | `owners` (`OwnerRepository`) | このコントローラが依存する `OwnerRepository` インスタンス。本メソッド内では直接使用されないが、呼び出し元の `processFindForm` → `findPaginatedForOwnersLastName` を通じてデータ取得の起点となる |

## 4. CRUD操作／呼び出しサービス

本メソッド自体は直接のデータベースアクセスや SC コード呼び出しを行いません。`Page<Owner>` は呼び出し元 (`findPaginatedForOwnersLastName` → `OwnerRepository.findByLastNameStartingWith`) によって既に取得済みのデータを処理する、View へのバインド専用メソッドです。

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `findPaginatedForOwnersLastName` | — | `owners` (Table) | 呼び出し元メソッドを通じて `OwnerRepository.findByLastNameStartingWith` を介し、`owners` テーブルからラストネームの前方一致検索を実行し、ページネーション結果を返す |

**補足 — 呼び出し元の内部操作:**

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `findByLastNameStartingWith` | — | `owners` (Table) | Spring Data JPA によって生成される動的クエリ。`lastname` パラメータの先頭一致で `owners` テーブルを検索し、`Pageable` 指定でページネーションを適用 |

## 5. 依存関係トレース

| # | 呼び出し元（画面/バッチ） | 呼び出しチェーン（本メソッドまでの全経路） | 終端（SC / CRUD / エンティティ） |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | Controller:OwnerController | `OwnerController.processFindForm` -> `OwnerController.findPaginatedForOwnersLastName` -> `OwnerController.addPaginationModel` | `findByLastNameStartingWith [R] owners` |

### 呼び出し先の詳細（本メソッドが直接呼び出すもの）

| # | 呼び出し先 | 呼び出し元 | 呼び出しチェーン | 終端（SC / CRUD / エンティティ） |
|---|-----------|-----------|--------------------------------------|-------------------------------|
| 1 | `Page.getContent()` | `addPaginationModel` | `addPaginationModel` -> `paginated.getContent()` | `owners` テーブルの現在のページ行を取得 [R] |
| 2 | `Page.getTotalPages()` | `addPaginationModel` | `addPaginationModel` -> `paginated.getTotalPages()` | ページネーションメタ情報 [R] |
| 3 | `Page.getTotalElements()` | `addPaginationModel` | `addPaginationModel` -> `paginated.getTotalElements()` | 検索結果の総件数メタ情報 [R] |
| 4 | `Model.addAttribute()` | `addPaginationModel` | `addPaginationModel` -> `model.addAttribute(...)` x4 | Spring MVC Model への属性格納 |

## 6. 分岐ごとの詳細ブロック

本メソッドには条件分岐（if/else/switch）が存在しないため、単一ブロックとして記述します。

**ブロック 1** — [EXEC / SET / RETURN] `(一連のモデル構築処理)` (L123)

> ページネーションされたオーナー検索結果を Spring MVC の Model にバインドし、ビュー描画の準備を行う。分岐なしの直列処理。

| # | 種別 | コード |
|---|------|------|
| 1 | SET | `List<Owner> listOwners = paginated.getContent()` — ページネーション結果から現在のページのオーナーエンティティリストを抽出 |
| 2 | EXEC | `model.addAttribute("currentPage", page)` — 現在のページ番号をモデルに追加。ビューのページネーションUIで現在地をハイライトするために使用 |
| 3 | EXEC | `model.addAttribute("totalPages", paginated.getTotalPages())` — 全体のページ数をモデルに追加。ページネーションバーの表示範囲を決定 |
| 4 | EXEC | `model.addAttribute("totalItems", paginated.getTotalElements())` — 検索結果の総件数をモデルに追加。一覧画面に「合計 N 件」と表示 |
| 5 | EXEC | `model.addAttribute("listOwners", listOwners)` — 抽出したオーナー一覧をモデルに追加。Thymeleaf テンプレートで `<tr>` ループ描画の対象となる |
| 6 | RETURN | `return "owners/ownersList"` — 表示テンプレートパスを返却。呼び出し元の `processFindForm` がこの文字列を使用して View をレンダリング |

## 7. 用語集

| 用語 | 種別 | 業務的意味 |
|------|------|----------------|
| `Owner` | Entity | クリニックの飼い主（オーナー）情報を表す JPA エンティティ。`Person` を継承し、名前・住所・都市・電話番号・説明・登録日などの属性を持つ |
| `Page<T>` | Type | Spring Data によるページネーションラッパークラス。単ページのコンテンツ (`getContent()`)、総ページ数 (`getTotalPages()`)、総要素数 (`getTotalElements()`) などを提供 |
| `Model` | Type | Spring MVC のモデルオブジェクト。Controller から View へのデータ受け渡し用。`addAttribute()` でキーと値のペアを追加すると、Thymeleaf テンプレートから参照可能になる |
| `processFindForm` | Method | オーナー検索フォームの送信を処理するエントリポイント。ラストネームでの検索実行、結果数に応じた分岐（0件: エラー表示 / 1件: 詳細ページへリダイレクト / 2件以上: 一覧表示）を行う |
| `findPaginatedForOwnersLastName` | Method | ラストネームの前方一致検索をページネーション付きで実行するメソッド。ページサイズは固定値 5。`OwnerRepository.findByLastNameStartingWith` をラップ |
| `owners/ownersList` | View | 検索結果のオーナー一覧を表示する Thymeleaf テンプレート。`currentPage`, `totalPages`, `totalItems`, `listOwners` の 4 属性を使用してページネーションUIとデータ一覧をレンダリング |
| `owners` | DB Table | オーナー情報を格納するデータベーステーブル。`@Table(name = "owners")` でマッピングされている |
| `findByLastNameStartingWith` | Repository Method | Spring Data JPA によって自動生成されるクエリメソッド。ラストネームの前方一致検索を実行し、`Pageable` によるページネーションを適用 |
| Page Size (5) | Constant | ページネーションの1ページあたりの表示件数。`findPaginatedForOwnersLastName` で `PageRequest.of(page - 1, 5)` として固定値 5 が使用される |
