# 業務ロジック — ClinicServiceTests.shouldUpdateOwner() [18 LOC]

| 項目 | 値 |
|-------|-------|
| 完全修飾名 | `org.springframework.samples.petclinic.service.ClinicServiceTests` |
| レイヤー | Test (JUnit integration test) |
| モジュール | `service` (パッケージ: `org.springframework.samples.petclinic.service`) |

## 1. 役割

### ClinicServiceTests.shouldUpdateOwner()

本メソッドは、Pet Clinic アプリケーションにおける「飼い主（Owner）情報の更新」機能の統合テストを実行する。具体的には、データベースに存在する飼い主の姓を更新し、JPA経由で正しく永続化されることを検証する。まず `OwnerRepository.findById()` で `id = 1` のオーナーレコードを取得し、元の姓（`lastName`）を退避した上で、文字列結合によって `"X"` を付加した新姓を生成する。その後、`setLastName()` でエンティティ上のフィールドを更新し、`OwnerRepository.save()` で差分をデータベースにコミットする。最後に、同じ `findById()` 再度実行してDBから最新値を取得し、更新後の姓が期待値と一致することをアサートすることで、Write-Read Consistency（書き込み後の整合性）を検証する。

本メソッドはテストメソッドであり、業務画面やバッチから直接呼び出されるエントリーポイントではない。代わりに、`@DataJpaTest` アノテーションが付与された Spring Boot の JPA 統合テストスイートの一部として、Owner エンティティの CRUD 操作が正しく動作することを保証する役割を持つ。`@Transactional` アノテーションにより、テスト実行中のDB変更は自動的にロールバックされ、テスト間の独立性が保たれる。

テストパターンは分岐を持たない単一流のパスであり、以下の3フェーズで構成される。Read Phase — DBからの既存オーナー取得、Update Phase — フィールド更新と `save()` による永続化、Verify Phase — 再取得による整合性検証。この設計により、Spring Data JPA の `CrudRepository` 実装が正しく機能することが検証される。

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

```mermaid
flowchart TD
    START(["shouldUpdateOwner()"])
    START --> LOAD["findById(1) - Owner読み込み"]
    LOAD --> ASSERT1["assertThat - 存在確認"]
    ASSERT1 --> GET["owner.getLastName() - 旧名取得"]
    GET --> NEW["newLastName = oldLastName + \"X\""]
    NEW --> SET["owner.setLastName(newLastName)"]
    SET --> SAVE["this.owners.save(owner)"]
    SAVE --> REFRESH["findById(1) - DB再取得"]
    REFRESH --> ASSERT2["assertThat - 存在確認"]
    ASSERT2 --> ASSERT3["assertThat - 新名と一致確認"]
    ASSERT3 --> END_NODE(["正常終了 / Next"])
```

本メソッドには条件分岐（if/else、switch）が存在しない。処理は全18行の単一シーケンシャルパスであり、以下の5つのステップで構成される。

1. **Read Owner** — `this.owners.findById(1)` で DB から ID=1 の Owner エンティティを `Optional<Owner>` として取得。
2. **Extract Old Value** — `owner.getLastName()` で既存姓を取得し、`oldLastName` 変数に保持。
3. **Transform** — 文字列連結 `"X"` を付加した新姓 `newLastName` を生成。
4. **Persist** — `owner.setLastName(newLastName)` でエンティティを更新後、`this.owners.save(owner)` で DB に反映。
5. **Verify** — `findById(1)` により再取得し、DB上の値が新姓と一致することを `assertThat()` で検証。

## 3. パラメータ分析

| No | パラメータ名 | 型 | 業務的説明 |
|----|---------------|------|---------------------|
| - | (該当なし) | - | 本メソッドはパラメータを持たない。テストメソッドとして固定のID（1）をハードコードしてテストする。 |

**インスタンスフィールドおよび外部状態:**

| 名前 | 型 | 業務的説明 |
|------|------|---------------------|
| `this.owners` | `OwnerRepository` | Spring Data JPA による Owner エンティティの永続化層。`findById()` および `save()` メソッドを通じて `owners` テーブルの読み書きを仲介する。Autowired でインジェクションされる。 |

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

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

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `OwnerRepository.findById(Integer)` | なし（Spring Data JPA 自動生成） | `owners` (Owner) | ID=1 の Owner エンティティを DB から検索する。Spring Data JPA の名前付きクエリ規約により、`findById` メソッド名から自動で SQL が生成される。 |
| U | `OwnerRepository.save(Owner)` | なし（Spring Data JPA 自動生成） | `owners` (Owner) | 更新された Owner エンティティを DB に永続化する。既存レコードの場合は UPDATE、新規の場合は INSERT を実行する。 |

**詳細分析:**

本メソッドは `OwnerRepository` インターフェースの標準的な Spring Data JPA メソッドのみを使用する。SCコード（Service Component Code）やCBS（CBS：バッチ処理コンポーネント）のパターンは本メソッドでは適用されない。これは Spring Boot の Pet Clinic サンプルアプリケーションであり、独自フレームワークの SC/CBS アーキテクチャではなく、Spring Data JPA のリポジトリパターンに準拠しているため。

| CRUD | SC / CBS | SCコード | エンティティ / DB | 操作の説明 |
|------|----------|---------|-------------|----------------------|
| R | `findById(Integer)` | — | `owners` テーブル, Owner エンティティ | DB上のオーナーレコードを主キーで一意取得。戻り値は `Optional<Owner>` 形式で、存在しない場合は空のOptionalが返る。 |
| U | `save(Owner)` | — | `owners` テーブル, Owner エンティティ | Ownerエンティティの変更を永続化。`@Transactional` の制御下で実行され、テスト終了時に自動的にロールバックされる。 |

**エンティティ詳細: `Owner`**

`Owner` エンティティは `Person` クラス（`BaseEntity` 継承）を親に持ち、`@Table(name = "owners")` で `owners` テーブルにマッピングされる。主なフィールドは以下の通り。

| フィールド名 | 型 | 制約 | 業務的意味 |
|-------------|------|------|-------------|
| `id` | `Integer` | `@Id`, `GENERATED BY IDENTITY` | 主キー — 自動採番 |
| `firstName` | `String` | `@NotBlank` (Person継承) | 名（ファーストネーム） |
| `lastName` | `String` | `@NotBlank` (Person継承) | 姓（ラストネーム）— 本テストで更新対象 |
| `address` | `String` | `@NotBlank` | 住所 |
| `city` | `String` | `@NotBlank` | 都市 |
| `telephone` | `String` | `@NotBlank`, `@Pattern("\\d{10}")` | 電話番号（10桁の数字） |

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

| # | 呼び出し元（画面/バッチ） | 呼び出しチェーン（本メソッドまでの全経路） | 終端（SC / CRUD / エンティティ） |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | テストランナー (JUnit 5) | `ClinicServiceTests.shouldUpdateOwner` | `findById [R] owners`, `save [U] owners` |

本メソッドは `@Test` アノテーション付きの JUnit 5 テストメソッドであり、実業務の画面やバッチ（CBS）、コントローラから呼び出されることは決してない。`search_files` により、`shouldUpdateOwner(` を呼び出す Java ファイルは全てのリポジトリ検索でゼロ件が返った。テストランナー（JUnit Platform）がテストスイート実行時に本メソッドを自動発見・呼び出す唯一的な呼び出し元となる。

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

本メソッドには条件分岐が存在しない。全処理は単一の直列ブロックとして記述する。

**ブロック 1** — [シーケンシャル処理] (L127)

> Ownerレコードの取得 → 姓の変更 → 永続化 → 再取得による検証。全18行からなる単一パスのテストフロー。

| # | 種別 | コード |
|---|------|------|
| 1 | CALL | `this.owners.findById(1)` | `Optional<Owner> optionalOwner = this.owners.findById(1);` — ID=1 の Owner を DB から取得 |
| 2 | EXEC | `assertThat(optionalOwner).isPresent()` | `assertThat(optionalOwner).isPresent();` — ID=1 の Owner が存在することを検証 |
| 3 | SET | `Owner owner = optionalOwner.get();` | `Owner owner = optionalOwner.get();` — Optional からオーナーエンティティを取得 |
| 4 | SET | `String oldLastName = owner.getLastName();` | `String oldLastName = owner.getLastName();` — 現在の姓を退避 |
| 5 | SET | `String newLastName = oldLastName + "X";` | `String newLastName = oldLastName + "X";` — 新姓を生成（"X"サフィックス付加） |
| 6 | EXEC | `owner.setLastName(newLastName);` | `owner.setLastName(newLastName);` — Ownerエンティティの姓を更新 |
| 7 | CALL | `this.owners.save(owner)` | `this.owners.save(owner);` — 更新されたOwnerをDBに永続化 |
| 8 | CALL | `this.owners.findById(1)` | `optionalOwner = this.owners.findById(1);` — DBから最新値を再取得 |
| 9 | EXEC | `assertThat(optionalOwner).isPresent()` | `assertThat(optionalOwner).isPresent();` — 再取得でOwnerが存在することを検証 |
| 10 | SET | `owner = optionalOwner.get();` | `owner = optionalOwner.get();` — Optionalから再取得したエンティティを取得 |
| 11 | EXEC | `assertThat(owner.getLastName()).isEqualTo(newLastName)` | `assertThat(owner.getLastName()).isEqualTo(newLastName);` — DBの姓が新姓と一致することを検証 |
| 12 | RETURN | (void) | テストメソッドの正常終了 |

## 7. 用語集

| 用語 | 種別 | 業務的意味 |
|------|------|------------------|
| `shouldUpdateOwner` | メソッド名 | 飼い主（Owner）情報の更新処理を検証するテストメソッド名。AssertJの命名規則に従う。 |
| `Owner` | Entity | ペットクリニックにおける「飼い主」を表すドメインエンティティ。氏名・住所・電話番号を保持し、複数のペット（Pet）を所有できる。 |
| `lastName` | Field | 飼い主の姓（ラストネーム）。`Person` 親クラスで定義され、`@NotBlank` 制約が付与される必須フィールド。 |
| `firstName` | Field | 飼い主の名（ファーストネーム）。`Person` 親クラスで定義される必須フィールド。 |
| `address` | Field | 飼い主の住所。`@NotBlank` 制約付き。 |
| `telephone` | Field | 飼い主の電話番号。`@Pattern("\\d{10}")` 制約で10桁の数字に制限される。 |
| `owners` | DB Table | `@Table(name = "owners")` でマッピングされるテーブル。Ownerエンティティの永続化先。 |
| `OwnerRepository` | Interface | Spring Data JPA のリポジトリインターフェース。`JpaRepository<Owner, Integer>` を継承し、OwnerエンティティのCRUD操作を定義する。 |
| `@Transactional` | アノテーション | テスト実行中にDB変更をトランザクションとして扱い、テスト終了後に自動ロールバックする。テスト間の独立性を保証する。 |
| `@Test` | アノテーション | JUnit 5 のテストメソッドマークアノテーション。テストランナーにより自動実行される。 |
| `@DataJpaTest` | アノテーション | Spring Boot のJPA統合テスト用アノテーション。Entity・Repositoryのコンポーネントのみをロードする。 |
| `@Autowired` | アノテーション | Spring IoCコンテナから依存性注入を行う。`owners` フィールドに `OwnerRepository` がインジェクションされる。 |
| `assertThat` | Assertion | AssertJ フレームワークのアサートメソッド。`isPresent()` はOptionalが空でないことを検証し、`isEqualTo()` は値の等価性を検証する。 |
| `findById` | Repository Method | Spring Data JPA によって自動生成される検索メソッド。主キーによる一意検索を実行する。 |
| `save` | Repository Method | Spring Data JPA によって自動生成される永続化メソッド。新規エンティティの場合はINSERT、既存の場合はUPDATEを実行する。 |
| JPA | 技術略語 | Java Persistence API — Java EE/Jakarta EE の ORM 標準仕様。Spring Data JPA の基盤技術。 |
| Spring Data JPA | 技術名称 | Spring Framework が提供するリポジトリ抽象化レイヤ。インターフェース名やメソッド名からSQLクエリを自動生成する。 |
| Pet Clinic | ドメイン名 | Spring 公式のサンプルWebアプリケーション。獣医クリニックの予約管理システムを模した、Spring Boot アプリケーション。 |
