# 業務ロジック — PetController.findOwner() [7 LOC]

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

## 1. 役割

### PetController.findOwner()

`PetController.findOwner()` は、スプリング MVC の `@ModelAttribute` アノテーションを付与されたモデル属性メソッドであり、獣医クリニックのオーナー（顧客）情報を ID を基に取得してビュー用のモデルにバインドする役割を担う。このメソッドは、URL パス `/owners/{ownerId}` で受信されたリクエストに対して、リクエストされたオーナーが存在するかどうかを検証し、存在する場合は `Owner` エンティティを返して Thymeleaf などのビューテンプレートが利用可能にする。存在しない場合には `IllegalArgumentException` をスローし、システムに対して ID の無効性を即座に通知する。

本メソッドは、ペットクリニック管理システムにおけるオーナー情報へのアクセスゲートウェイとして機能する。`@Controller` クラス `PetController` は `/owners/{ownerId}` でマッピングされており、オーナー固有のペット一覧表示、ペット情報作成・編集フォームなどの画面で利用される。`@ModelAttribute` により、これらの画面リクエストに対して自動的・暗黙的にオーナーデータが取得され、コントローラのアクションメソッドが実行される前にモデルに設定される。

このメソッドは「ルーティング / ディスパッチ」パターンの一種として、URL から抽出されたパス変数（`ownerId`）を受け取り、永続層（`OwnerRepository`）を通じて対応するエンティティをフェッチする。より大きなシステムにおける役割は、オーナー情報への一元化したアクセス層を提供し、各ビューで毎回重複したデータ取得ロジックを書く必要を排除することである。

条件分岐は一つのみ：`Optional<Owner>` が値を保持するか（オーナーが存在）あるいは空である（オーナーが存在しない）の2分岐であり、前者ではエンティティをそのまま返し、後者では例外をスローする。

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

```mermaid
flowchart TD
    START(["findOwner(ownerId)"])
    START --> CALL_DB["CALL: this.owners.findById(ownerId)"]
    CALL_DB --> CHECK["optionalOwner.isPresent?"]
    CHECK --> |true| GET["GET: optionalOwner.get()"]
    CHECK --> |false| THROW["THROW: IllegalArgumentException"]
    GET --> RETURN_OWNER(["Return: Owner"])
    RETURN_OWNER --> END(["end"])
    THROW --> END
```

| 処理ステップ | ノード種別 | 説明 |
|-------------|-----------|------|
| 1 | CALL | `this.owners.findById(ownerId)` — `OwnerRepository` の JPA `findById` メソッドを呼び出し、`Optional<Owner>` を取得 |
| 2 | CHECK | `optionalOwner.isPresent()` — オーナーが見つかったかを確認（分岐） |
| 3 | GET | `optionalOwner.get()` — 存在する場合、`Owner` エンティティをアンラップ |
| 4 | THROW | 存在しない場合、`IllegalArgumentException` をスロー（メッセージには `ownerId` とエラー通知が含まれる） |
| 5 | RETURN | アンラップした `Owner` オブジェクトを返却（モデル属性としてビューにバインド） |

## 3. パラメータ分析

| No | パラメータ名 | 型 | 業務的説明 |
|----|---------------|------|---------------------|
| 1 | `ownerId` | `@PathVariable("ownerId") int` | クリニックに登録されているオーナー（顧客）を識別する主キー。URL パス `/owners/{ownerId}` から自動抽出され、該当オーナーのペット情報一覧やペット作成・編集フォームの表示に使用される。正の整数値を取り、存在しない ID が渡されると `IllegalArgumentException` がスローされる |

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

| フィールド | 型 | 業務的説明 |
|-----------|------|---------------------|
| `owners` | `OwnerRepository` | オーナー情報の永続層アクセスを担当する Spring Data JPA リポジトリ。`findById` メソッドを通じてデータベースのオーナーテーブルを検索する |

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

### コード解析グラフからの事前抽出エビデンス:

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `OwnerRepository.findById` | OwnerRepository | `Owner` | JPA `findById` メソッドにより、指定された ID に一致する `Owner` エンティティをデータベースから読み込み。結果は `Optional<Owner>` として返される |

**分類の根拠:**
- **R (Read)**: `findById` は Spring Data JPA の標準的な読み取り操作メソッドであり、主キーによる単一エンティティの検索を行う。CREATE / UPDATE / DELETE のいずれの操作も行わない。
- **エンティティ**: `OwnerRepository` は `JpaRepository<Owner, Integer>` を継承しており、対象エンティティは `Owner` クラス。`Owner` は `Person` クラスを継承するクライアント（ペットの所有者）情報を表す JPA エンティティ。

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

本メソッドは `@ModelAttribute` により Spring MVC によって暗黙的に呼び出される。以下に呼び出し元となるコントローラと関連する呼び出しチェーンを示す。

| # | 呼び出し元（画面/バッチ） | 呼び出しチェーン（本メソッドまでの全経路） | 終端（SC / CRUD / エンティティ） |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | Controller: PetController | `@ModelAttribute` 自動起動（URL `/owners/{ownerId}` 受信時） -> `PetController.findOwner` | `findById [R] Owner` |
| 2 | Controller: OwnerController | `@ModelAttribute` 自動起動（URL `/owners/{ownerId}` 受信時） -> `OwnerController.findOwner` | `findById [R] Owner` |

**注釈:**
- `PetController.findOwner()` と `OwnerController.findOwner()` の両方に同名の `@ModelAttribute` メソッドが存在する。両方とも同じシグネチャ `findOwner(@PathVariable("ownerId") int ownerId)` で、`OwnerRepository.findById()` を呼び出す。
- Spring MVC の `@ModelAttribute` メソッドは、`@GetMapping` / `@PostMapping` などのアクションメソッドが実行される**前**に自動呼び出される。そのため、`/owners/{ownerId}` で始まるすべてのリクエスト（ペット一覧表示、ペット作成フォーム、ペット編集フォームなど）において、このメソッドが暗黙的に起動される。
- テストクラス `OwnerControllerTests` においても `owners/findOwners` ビューへの遷移テストが行われており、間接的にこのメソッドの呼び出しが検証されている。

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

**Block 1** — [CALL] `this.owners.findById(ownerId)` (L67)

> `OwnerRepository.findById()` を呼び出して、指定された ID に一致するオーナーを検索する。

| # | 種別 | コード |
|---|------|------|
| 1 | CALL | `this.owners.findById(ownerId)` |
| 2 | SET | `optionalOwner = Optional<Owner>` の戻り値を代入 |

**Block 2** — [IF / ELSE] `optionalOwner.isPresent()` (L68–L69)

> オーナーが存在する場合はエンティティを取得し、存在しない場合は例外をスローする2分岐。

| # | 種別 | コード |
|---|------|------|
| 1 | SET | `owner = optionalOwner.orElseThrow(...)` — 存在時は `Owner` をアンラップして代入 |
| 2 | SET | `owner = optionalOwner.orElseThrow(...)` — 存在しない場合は `IllegalArgumentException` を生成 |

**Block 2.1** — [条件: true] `optionalOwner.isPresent()` (L68)

> オーナーが存在する場合。`orElseThrow` のラムダ式は評価されず、`Owner` オブジェクトが取得される。

| # | 種別 | コード |
|---|------|------|
| 1 | SET | `owner = optionalOwner.orElseThrow(ラムダ)` — アンラップされた `Owner` を取得 |

**Block 2.2** — [条件: false] `optionalOwner.isPresent()` (L68–L69)

> オーナーが存在しない場合。`orElseThrow` のラムダ式が評価され、`IllegalArgumentException` がスローされる。エラーメッセージには「Owner not found with id: {ownerId}. Please ensure the ID is correct」が含まれる。

| # | 種別 | コード |
|---|------|------|
| 1 | THROW | `optionalOwner.orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId + ". Please ensure the ID is correct "))` |

**Block 3** — [RETURN] (L70)

> アンラップされた `Owner` オブジェクトを返却し、モデル属性としてビューにバインドする。

| # | 種別 | コード |
|---|------|------|
| 1 | RETURN | `return owner;` — 検索結果の `Owner` エンティティを返す |

## 7. 用語集

| 用語 | 種別 | 業務的意味 |
|------|------|------------------|
| `Owner` | Entity | クリニックの顧客（ペットの所有者）を表す JPA エンティティ。`Person` クラスを継承し、名前、住所、電話番号などの基本情報を保持する |
| `Person` | Entity | `Owner` の基底クラス。氏名や連絡先などの個人情報を含む |
| `OwnerRepository` | Interface | Spring Data JPA のリポジトリインターフェース。`JpaRepository<Owner, Integer>` を継承し、オーナーエンティティの CRUD 操作を提供する |
| `JpaRepository` | Interface | Spring Data JPA の標準リポジトリインターフェース。エンティティの主キーによる検索、保存、削除などの基本 CRUD メソッドを提供する |
| `@ModelAttribute` | Annotation | コントローラのアクションメソッドが実行される前に自動的に呼び出され、モデルに属性をバインドするスプリング MVC アノテーション |
| `@PathVariable` | Annotation | URL パスのプレースホルダ（例: `/owners/{ownerId}`）から値を抽出してメソッドパラメータにバインドするスプリング MVC アノテーション |
| `Optional` | Java Type | オブジェクトが null の可能性がある場合に null 以外の値をラップする Java 8 の標準クラス。本メソッドでは存在/非存在の2状態を安全に表現する |
| `IllegalArgumentException` | Java Exception | 不正な引数が渡された際にスローされるJavaの標準例外。本メソッドでは存在しないオーナー ID でアクセスされた場合に使用される |
| `PetController` | Controller | `/owners/{ownerId}` でマッピングされるスプリング MVC コントローラ。オーナー固有のペット操作（一覧表示、作成、編集）を扱う |
| `OwnerController` | Controller | オーナーの新規登録、検索、編集などのオーナー情報全体を扱うスプリング MVC コントローラ |
| `/owners/{ownerId}` | URL Pattern | オーナー固有のリソースへの URL パターン。`{ownerId}` はパス変数として抽出され、オーナー識別に使用される |
