# 業務ロジック — OwnerController.processUpdateOwnerForm() [19 LOC]

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

## 1. 役割

### OwnerController.processUpdateOwnerForm()

このメソッドは、Spring PetClinic アプリケーションにおける「ペットオーナー（所有者）情報の更新」を扱う Web フォームのサブミット処理エントリポイントである。URL パス `/owners/{ownerId}/edit` に対して POST リクエストが発生した際に、Spring MVC がこのメソッドを呼び出す。

業務的には、オーナーの基本情報（名前、住所、都市、電話番号）および保有するペット情報の更新トランザクションを中核的に制御する。具体的には、① フォーム検証エラーの検知とエラーメッセージのフラッシュ属性への格納、② フォーム内で入力されたオーナーIDとURLパスパラメータのIDとの不一致チェック（なりすまし・改竄防止）、③ 検証通過後の `Owner` エンティティの永続化、④ 成功時のリダイレクトと承認メッセージの表示 — の4つの主要なブランチで構成される。

デザインパターンとしては、Spring MVC の標準的な POST-Redirect-GET パターンを実装しており、リダイレクト属性（`RedirectAttributes`）を用いたフラッシュスコープへのメッセージ渡しまでを一手に担う。システム全体では、オーナー管理画面からの唯一の更新エンドポイントとして位置付けられ、ユーザーがオーナー情報を編集フォームからデータを提出した際の全経路をこの1メソッドが吸収する。

条件分岐は2つの検証パスに分かれる。第1のパスでは `BindingResult` のエラー有無を判定し、エラーが存在する場合は元の更新フォーム画面へ戻ってエラーメッセージを表示する。第2のパスでは、フォーム送信データに含まれる `owner.getId()` と URL パラメータ `ownerId` が一致するかを `Objects.equals()` で照合し、不一致の場合は所有者IDのズレをユーザーへ通知した上で、修正後のURLへリダイレクトする。両方の検証をクリアした場合のみ、実際の永続化処理が実行される。

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

```mermaid
flowchart TD
    START(["processUpdateOwnerForm 開始"])
    START --> VERIFY["検証チェック:
result.hasErrors()"]
    VERIFY -->|true| ERR_MSG["フラッシュ属性設定:
addFlashAttribute('error', 'Update Error')"]
    ERR_MSG --> FORM_BACK["戻り値: VIEWS_OWNER_CREATE_OR_UPDATE_FORM
(編集フォーム画面へ戻す)"]
    VERIFY -->|false| ID_CHECK["不一致チェック:
!Objects.equals(owner.getId(), ownerId)"]
    ID_CHECK -->|true| ID_ERR["フラッシュ属性設定:
addFlashAttribute('error', 'Owner ID mismatch')
result.rejectValue('id', 'mismatch', ...)
リダイレクト: /owners/{ownerId}/edit"]
    ID_CHECK -->|false| SET_ID["owner.setId(ownerId)
(パスパラメータのIDをエンティティに設定)"]
    SET_ID --> SAVE["owners.save(owner)
(Owner リポジトリによる永続化)"]
    SAVE --> SUCCESS_MSG["フラッシュ属性設定:
addFlashAttribute('message', 'Owner Values Updated')"]
    SUCCESS_MSG --> REDIRECT["戻り値: redirect:/owners/{ownerId}
(オーナー詳細画面へ遷移)"]
    FORM_BACK --> END(["処理終了"])
    ID_ERR --> END
    REDIRECT --> END
```

**定数解決:**
- `VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm"` — 編集フォームのビュー名。検証エラー時にこのビューテンプレートにフォールバックし、ユーザーに入力修正を促す。

## 3. パラメータ分析

| No | パラメータ名 | 型 | 業務的説明 |
|----|---------------|------|---------------------------|
| 1 | `owner` | `@Valid Owner` | 更新対象のオーナー情報。フォームから送信された基本情報（氏名、住所、都市、電話番号）および保有ペット情報のリストを含む。`@Valid` アノテーションにより、Jakarta Validation の制約（`@NotBlank`, `@Pattern` など）が自動的に適用され、違反があれば `BindingResult` に収集される。 |
| 2 | `result` | `BindingResult` | バリデーション結果の格納先。フォーム入力の検証エラーの有無（`hasErrors()`）を判定するために使用される。エラーが存在する場合は、元の編集フォーム画面へ戻り、ユーザーへエラー表示を行う。 |
| 3 | `ownerId` | `@PathVariable("ownerId") int` | URL パス `/owners/{ownerId}/edit` から抽出される、更新対象のオーナーを特定する主キー。フォーム内の入力値と不一致でないかを検証し、整合性が取れた場合に `owner.setId(ownerId)` によってエンティティのIDを設定する。 |
| 4 | `redirectAttributes` | `RedirectAttributes` | リダイレクト後の次のリクエストで参照できるフラッシュスコープへ属性（メッセージやエラー）を一時的に格納するためのオブジェクト。成功メッセージやエラーメッセージを渡す用途で使用する。 |

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

| フィールド名 | 型 | 業務的説明 |
|-------------|------|------------------|
| `owners` | `JpaOwnerRepositoryImpl` | オーナー情報の永続化を担うSpring Data JPAリポジトリ。`save()` メソッドにより `owners` テーブルへのINSERT/UPDATE実行を行う。 |

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

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

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `BindingResult.hasErrors` | BindingResult | - | フォームバリデーションエラーの有無を判定する |
| R | `Objects.equals` | Objects | - | 2つのオブジェクト参照値をnull安全に比較する |
| U | `BindingResult.rejectValue` | BindingResult | - | フィールドエラーを result に追加する |
| U | `Owner.setId` | Owner | - | オーナーエンティティの主キーIDを設定する |
| U | `RedirectAttributes.addFlashAttribute` | RedirectAttributes | - | フラッシュスコープへメッセージ属性を格納する |
| C/R/U | `owners.save(owner)` | JpaOwnerRepositoryImpl | `owners` テーブル | Spring Data JPA による Owner エンティティの永続化（INSERT または UPDATE） |

**永続化詳細:**
- `JpaOwnerRepositoryImpl.save(Owner)` — Spring Data JPA の `CrudRepository.save()` を介して、`@Entity @Table(name = "owners")` でマッピングされた `owners` テーブルに対して、エンティティの新旧判定を行い、新規登録（INSERT）または更新（UPDATE）を実行する。`Owner` エンティティは `@OneToMany` で関連する `pets` テーブルも一括更新対象となる（`cascade = CascadeType.ALL`）。

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

| # | 呼び出し元（画面/バッチ） | 呼び出しチェーン（本メソッドまでの全経路） | 終端（SC / CRUD / エンティティ） |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | Controller: OwnerController | Spring DispatcherServlet → `@PostMapping("/owners/{ownerId}/edit")` → `processUpdateOwnerForm` | `save(owner) [R/U] owners` テーブル |
| 2 | View: owners/createOrUpdateOwnerForm | ユーザーがブラウザでフォームをサブミット → HTTP POST /owners/{ownerId}/edit → DispatcherServlet → `processUpdateOwnerForm` | `save(owner) [R/U] owners` テーブル |

**注釈:** 本メソッドは `@PostMapping` アノテーションでHTTP POSTエンドポイントとして登録されており、直接コード内で他のメソッドから呼び出されているわけではない。呼び出しはすべてSpring MVCのHTTPリクエストディスパッチャ（DispatcherServlet）を介して行われる。画面呼び出し元としては、オーナー編集フォームビュー `owners/createOrUpdateOwnerForm` からのユーザーサブミットが唯一のエントリポイントである。

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

**ブロック 1** — [IF] `(result.hasErrors())` (L147)

> フォーム検証エラーが存在する場合、エラーメッセージをフラッシュ属性へ格納し、元の編集フォームへ戻す。ユーザーは入力修正を行った後、再度サブミットを行う。

| # | 種別 | コード |
|---|------|------|
| 1 | EXEC | `result.hasErrors()` — バリデーション結果からエラーの有無をチェック |
| 2 | CALL | `redirectAttributes.addFlashAttribute("error", "There was an error in updating the owner.")` — フラッシュスコープにエラーメッセージを設定 |
| 3 | RETURN | `return VIEWS_OWNER_CREATE_OR_UPDATE_FORM` — 編集フォームビュー名を返す（画面戻り） |

**ブロック 2** — [IF] `(!Objects.equals(owner.getId(), ownerId))` (L152)

> フォーム内のオーナーIDとURLパスパラメータのIDが一致しない場合。なりすましやURL改竄による不正な更新を試みるケースを検知し、エラーメッセージを表示した上でリダイレクトする。

| # | 種別 | コード |
|---|------|------|
| 1 | SET | `Objects.equals(owner.getId(), ownerId)` — フォーム送信IDとパスパラメータIDを比較 |
| 2 | EXEC | `result.rejectValue("id", "mismatch", "The owner ID in the form does not match the URL.")` — フィールドエラーを `id` 属性に追加 |
| 3 | CALL | `redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again.")` — フラッシュスコープに一致エラーメッセージを設定 |
| 4 | RETURN | `return "redirect:/owners/{ownerId}/edit"` — URLリダイレクトを実行 |

**ブロック 3** — [ELSE / 正常パス] (L156)

> 両方の検証（エラーの有無、ID の不一致）をクリアした正規の更新パス。エンティティIDを設定し、リポジトリへ永続化を委譲した後、成功メッセージとともにオーナー詳細画面へリダイレクトする。

| # | 種別 | コード |
|---|------|------|
| 1 | SET | `owner.setId(ownerId)` — URL パスパラメータのIDをOwnerエンティティに設定し、整合性を保証 |
| 2 | CALL | `this.owners.save(owner)` — Ownerリポジトリへエンティティの永続化を委譲（`owners` テーブルのUPDATE） |
| 3 | CALL | `redirectAttributes.addFlashAttribute("message", "Owner Values Updated")` — フラッシュスコープに成功メッセージを設定 |
| 4 | RETURN | `return "redirect:/owners/{ownerId}"` — オーナー詳細画面へリダイレクト（POST-Redirect-GET パターン） |

## 7. 用語集

| 用語 | 種別 | 業務的意味 |
|------|------|------------------|
| `Owner` | Entity | ペットオーナー（動物の所有者）エンティティ。`owners` テーブルにマッピングされ、名前、住所、都市、電話番号、および保有ペットの一覧を含む。 |
| `owners` | テーブル名 | オーナー情報を格納するデータベーステーブル。主キー `id` を持つ。 |
| `pets` | 関連エンティティ | Owner と 1 対多 で関連するペットエンティティのリスト。`cascade = CascadeType.ALL` によりオーナーの保存時に一緒に永続化される。 |
| `BindingResult` | Spring MVC | コントローラのフォームパラメータに対するバリデーション結果を格納するインタフェース。`hasErrors()` でエラーの有無を判定する。 |
| `RedirectAttributes` | Spring MVC | リダイレクト時のフラッシュスコープへ一時的な属性（メッセージなど）を渡すためのクラス。 |
| `VIEWS_OWNER_CREATE_OR_UPDATE_FORM` | 定数 | 値: `"owners/createOrUpdateOwnerForm"` — オーナーの作成・編集フォームのビュー名。 |
| `@PostMapping` | アノテーション | HTTP POST リクエストを指定パスにマッピングするSpring MVC アノテーション。`/owners/{ownerId}/edit` へのPOSTをこのメソッドにルーティングする。 |
| `@Valid` | アノテーション | Jakarta Bean Validation を適用するアノテーション。メソッドパラメータに付与することで、リクエストボディのバリデーションが自動実行される。 |
| `@PathVariable` | アノテーション | URLパスのパラメータ部分をメソッド引数にバインドするSpring MVC アノテーション。`ownerId` パラメータとしてURLの `{ownerId}` 部分を抽出する。 |
| POST-Redirect-GET | パターン | フォームサブミット後にGETリクエストでリダイレクトし、ブラウザの再サブミットを防ぐWeb開発のパターン。このメソッドはこれを完全実装している。 |
| `JpaOwnerRepositoryImpl` | Repository | Spring Data JPA を基盤とするオーナー永続化リポジトリ実装。`save()`, `findById()`, `findByLastNameStartingWith()` などの標準メソッドを提供する。 |
