---
description: "詳細設計 — PetController.findPet()"
---

# 業務ロジック — PetController.findPet() [13 LOC]

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

## 1. 役割

### PetController.findPet()

本メソッドは、Spring MVCの`@ModelAttribute`アノテーションにより、所有者（Owner）単位のペット管理画面へ`pet`モデル属性を供給するためのデータ準備メソッドである。動物病院情報システム（PetClinic）における「ペット登録・編集」画面の表示時に、新規作成時と既存ペットの表示時という2つの業務シナリオを、単一のエントリポイントで処理するルーティング（ディスパッチ）パターンを採用している。

`petId`がnullの場合は新規ペットオブジェクト（`Pet`）を生成して返す。これは「新規ペット登録」画面の初期表示時であり、所有者情報の下に空のペットフォームをレンダリングするための前準備である。`petId`が設定されている場合は、`OwnerRepository`を用いて指定された所有者をデータベースから検索し、所有者の存在を検証した上で、その所有者に属する特定ペットのエンティティを取得する。これは「ペット情報編集」画面の表示時に、既存のペットデータ（名前、生年月日、種類など）をフォームにバインドするための処理である。

設計パターンとしては、Spring MVCの`@ModelAttribute`によるコントローラーフックを活用した**ビルダーパターン**の一種であり、コントローラーの処理を分割し、画面遷移を一元管理する役割を持つ。また、所有者に対するペットの参照整合性を`Owner.getPet(petId)`により強制することで、ドメインモデル内でのリソース分離を担保している。

システム全体の文脈では、`/owners/{ownerId}/pets/create`および`/owners/{ownerId}/pets/{petId}/edit`の両画面から依存しており、OwnerContext内のペット操作画面群の共通前処理として機能する。

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

```mermaid
flowchart TD
    START(["findPet(ownerId, petId)"])

    START --> COND_PET_ID{"petId is null?"}

    COND_PET_ID -->|"yes"| NEW_PET["new Pet()"]
    NEW_PET --> RETURN_NEW(["Return Pet (empty)"])

    COND_PET_ID -->|"no"| FIND_OWNER["this.owners.findById(ownerId)"]
    FIND_OWNER --> CHECK_OWNER{"Owner found?"}

    CHECK_OWNER -->|"no"| THROW_ERROR["throw IllegalArgumentException"]
    THROW_ERROR --> ERR_END(["異常終了"])

    CHECK_OWNER -->|"yes"| GET_PET["owner.getPet(petId)"]
    GET_PET --> RETURN_EXISTING(["Return Pet (existing)"])

    RETURN_NEW --> END(["End"])
    RETURN_EXISTING --> END
```

## 3. パラメータ分析

| No | パラメータ名 | 型 | 業務的説明 |
|----|---------------|------|---------------------|
| 1 | `ownerId` | `@PathVariable("ownerId") int` | 所有者（オーナー）の一意識別子。URLパス`/owners/{ownerId}`から抽出され、対象の動物病院登録オーナーを特定するための整数キー。この値に基づいて`OwnerRepository.findById()`が所有者エンティティを検索する。正の整数が想定され、存在しないIDが渡された場合は`IllegalArgumentException`が発生する。 |
| 2 | `petId` | `@PathVariable(name = "petId", required = false) Integer` | ペットの識別子。オプション（required=false）。nullの場合は「新規ペット作成」モードとして動作し、空の`Pet`インスタンスを生成して返す。整数値が渡された場合は「既存ペット編集」モードとして動作し、該当IDのペットエンティティを検索して返す。`Integer`型（ラッパー型）であるためnull許容となっている。 |

**読み込まれるインスタンスフィールド:**

| フィールド名 | 型 | 業務的説明 |
|--------------|------|---------------------|
| `this.owners` | `OwnerRepository` | 所有者エンティティのデータアクセスオブジェクト。JPAの`JpaRepository<Owner, Integer>`を実装し、データベースからの所有者検索を担う。 |

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

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

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `OwnerRepository.findById` | `OwnerRepository` | `owners` (Owner) | `OwnerRepository.findById(ownerId)`により、指定IDの所有者エンティティをデータベースから取得 |
| R | `Owner.getPet` | `Owner` | - | 所有者が保持するペット一覧（`LinkedHashSet<Pet>`）から指定IDのペットを線形探索して取得 |

**操作分類の詳細:**

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `this.owners.findById` | `OwnerRepository` | owners (Owner) | JPA RepositoryによるOwnerエンティティのID一意検索。データベースのownersテーブルから対象レコードを取得。所有者が存在しない場合はOptional.empty()が返る。 |
| R | `owner.getPet(petId)` | `Owner` | pets (in-memory) | 所有者ドメインオブジェクト内部のペットコレクションに対するIDマッチ検索。データベースクエリを伴わず、Javaコレクション内での線形探索により該当ペットを返す。新規保存済みでないペットのみが対象。 |

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

| # | 呼び出し元（画面/バッチ） | 呼び出しチェーン（本メソッドまでの全経路） | 終端（SC / CRUD / エンティティ） |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | Spring MVC (`@ModelAttribute`) | `DispatcherServlet` -> `PetController.findPet` | `findById [R] owners`<br>`getPet [R] pets` |

**補足:** 本メソッドはSpring MVCの`@ModelAttribute`アノテーションにより、コントローラーの各リクエストハンドラー（`@GetMapping`/`@PostMapping`でマッピングされたメソッド）実行前に自動的に呼び出されるフレームワークレベルのフックである。具体的なUI画面からの直接的な呼び出しチェーンは存在しない。`/owners/{ownerId}`で始まるすべてのリクエスト（ペット一覧表示、新規作成、編集、削除など）で本メソッドが実行される。

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

**Block 1** — IF `(petId == null)` (L76)

> petIdがnullの場合。新規ペット作成時の初期化処理を行う。`@ModelAttribute`によりモデル属性名`"pet"`に空のPetオブジェクトが設定される。

| # | 種別 | コード |
|---|------|------|
| 1 | NEW | `new Pet()` |
| 2 | RETURN | `return new Pet();` // 空のペットオブジェクトを返す（新規作成用） |

**Block 2** — IF-ELSE `(petId != null)` (L76)

> petIdがnullでない場合。既存ペットの検索処理を行う。

| # | 種別 | コード |
|---|------|------|
| 1 | CALL | `this.owners.findById(ownerId)` // OwnerRepository.findByIdにより所有者を検索。戻り値はOptional<Owner> |
| 2 | EXEC | `optionalOwner.orElseThrow(...)` // Ownerが存在しない場合はIllegalArgumentExceptionをスロー |

**Block 2.1** — THROW `(orElseThrow)` (L80)

> Ownerが見つからなかった場合の例外送出。

| # | 種別 | コード |
|---|------|------|
| 1 | THROW | `throw new IllegalArgumentException("Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")` // 不正な所有者IDを検出。呼び出し元に400エラーを返す |

**Block 2.2** — RETURN `(Owner exists)` (L82)

> Ownerが見つかった場合。その所有者に属するペットを取得する。

| # | 種別 | コード |
|---|------|------|
| 1 | CALL | `owner.getPet(petId)` // 所有者ドメインオブジェクトのgetPetメソッドを呼び出し、該当IDのペットをコレクションから検索 |
| 2 | RETURN | `return owner.getPet(petId);` // 検索結果のPetを返す。該当ペットが存在しない場合はnullが返る |

## 7. 用語集

| 用語 | 種別 | 業務的意味 |
|------|------|---------------------|
| `@ModelAttribute` | アノテーション | Spring MVCのコントローラーメソッドに付与し、リクエスト処理前にモデル属性を事前に準備・設定するためのフック機能。本メソッドでは画面表示前に`pet`オブジェクトをモデルに注入する。 |
| `@PathVariable` | アノテーション | HTTPリクエストのURLパス変数（`{ownerId}`, `{petId}`）をメソッドの引数にバインドするためのアノテーション。 |
| `Owner` | Entity | 動物病院システムにおける顧客（オーナー）を表すドメインエンティティ。ペットを1対多で保持する。 |
| `Pet` | Entity | ペット（飼い犬・飼い猫など）を表すドメインエンティティ。名前、生年月日、種類などの属性を持つ。DBテーブル名は`pets`。 |
| `OwnerRepository` | Repository | 所有者エンティティのデータアクセスレイヤー。Spring Data JPAの`JpaRepository<Owner, Integer>`を実装。 |
| `petId` | Field | ペット識別子。DBのpetsテーブルにおける主キーに相当する整数。新規作成時はnull。 |
| `ownerId` | Field | 所有者識別子。DBのownersテーブルにおける主キーに相当する整数。URLパスから取得。 |
| `IllegalArgumentException` | Exception | 不正な引数が渡された場合にスローされるランタイム例外。所有者IDが存在しない場合に発生。 |
| `Optional<Owner>` | Type | Java 8のOptional型。所有者検索の結果が存在する場合は`Optional.of(owner)`、存在しない場合は`Optional.empty()`として返す。 |
| `new Pet()` | Constructor | 新規ペットインスタンスの生成。DBには未保存であり、フォームの入力データがバインドされてから保存される。 |
