JPAの Persistence Cascade

### JPAにおける永続性の伝播（Cascade)

JPAでは、親エンティティ（`Order`）と子エンティティ（`OrderProduct`）、および関連するエンティティ（`Product`）の関係において、**永続性の伝播（Cascade）** は、親エンティティと一緒に子エンティティが自動的に保存、更新、削除されるようにするために使用されます。もし永続性の伝播が設定されていないと、関連するエンティティを手動でそれぞれ保存する必要があり、問題が発生することがあります。

### 永続性の伝播（Cascade）に関する主な原則

1. **Cascade設定**：親エンティティと子エンティティ間の関係で永続性の伝播（Cascade）を設定し、親エンティティが保存される際に子エンティティも自動的に保存されるようにします。`CascadeType.PERSIST` や `CascadeType.ALL` を使用することで、親エンティティが保存される際に子エンティティも自動的に保存されます。

2. **リレーションの設定**：適切に一方向または双方向の関連関係を設定し、関連するエンティティのフィールド名が正確に一致していることを確認します。

### サンプルコード

以下のサンプルコードは、親エンティティである `PurchaseOrder` と子エンティティである `PurchaseOrderItem`、そして他の関連エンティティである `InventoryItem` 間で永続性の伝播を設定する方法を示しています。

#### 1. `PurchaseOrder` エンティティ
`PurchaseOrder` は複数の `PurchaseOrderItem` と関連しており、永続性の伝播により `PurchaseOrder` が保存されると `PurchaseOrderItem` も一緒に保存されます。

```java
@Entity
public class PurchaseOrder {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "purchaseOrder", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<PurchaseOrderItem> orderItems = new ArrayList<>();

    public void addOrderItem(PurchaseOrderItem item) {
        orderItems.add(item);
        item.setPurchaseOrder(this);
    }

    // Getters and Setters
}
```

#### 2. `PurchaseOrderItem` エンティティ
`PurchaseOrderItem` は `PurchaseOrder` および `InventoryItem` と関連しており、`PurchaseOrder` との関係で `ManyToOne` として設定されています。

```java
@Entity
public class PurchaseOrderItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "purchase_order_id")
    private PurchaseOrder purchaseOrder;

    @ManyToOne
    @JoinColumn(name = "inventory_item_id")
    private InventoryItem inventoryItem;

    private int quantity;

    // Getters and Setters
}
```

#### 3. `InventoryItem` エンティティ
`InventoryItem` は永続性の伝播を必要としないため、`Cascade` 設定は不要です。

```java
@Entity
public class InventoryItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private int stockQuantity;

    // 在庫管理のビジネスロジック
    public void reduceStock(int quantity) {
        if (this.stockQuantity < quantity) {
            throw new InsufficientStockException("Not enough stock");
        }
        this.stockQuantity -= quantity;
    }

    // Getters and Setters
}
```

### 4. サービスでの永続性の伝播処理

サービス層で `PurchaseOrder` とそれに関連する `PurchaseOrderItem` を一緒に保存できるように、永続性の伝播を使用します。`PurchaseOrder` のみを保存すれば、`PurchaseOrderItem` も一緒に保存されます。また、`InventoryItem` は別途保存または管理できます。

```java
@Service
public class OrderService {

    @Autowired
    private PurchaseOrderRepository purchaseOrderRepository;

    @Autowired
    private InventoryItemRepository inventoryItemRepository;

    public void createOrder(OrderRequest orderRequest) {
        PurchaseOrder order = new PurchaseOrder();

        for (OrderItemRequest itemRequest : orderRequest.getItems()) {
            InventoryItem inventoryItem = inventoryItemRepository.findById(itemRequest.getItemId())
                    .orElseThrow(() -> new ItemNotFoundException("Item not found"));

            // 在庫を減らす
            inventoryItem.reduceStock(itemRequest.getQuantity());

            // 注文アイテムを作成
            PurchaseOrderItem orderItem = new PurchaseOrderItem();
            orderItem.setInventoryItem(inventoryItem);
            orderItem.setQuantity(itemRequest.getQuantity());

            // PurchaseOrderに追加
            order.addOrderItem(orderItem);
        }

        // PurchaseOrderと関連するPurchaseOrderItemを一緒に保存
        purchaseOrderRepository.save(order);
    }
}
```

### 双方向リレーションで `this` を使用する理由

関連オブジェクトを設定する際に、双方向のリレーションでは `this` を使用してオブジェクト間の**双方向の関係**を同期することが重要です。これにより、両側の関係が一貫して維持され、JPAが正しく管理できるようになります。ただし、すべてのリレーションで `this` を使用する必要があるわけではなく、主に**双方向リレーション**でオブジェクト間の**参照**を相互に設定する際に使用されます。

### 1. **双方向リレーションで `this` を使用する理由**
- **双方向リレーション**では、一方のエンティティが他方のエンティティを参照し、逆にもう一方も最初のエンティティを再び参照します。例えば、`Order` と `OrderItem` の関係では、`Order` は複数の `OrderItem` を持ち、各 `OrderItem` は再び `Order` を参照する構造です。
- このとき、一方だけで値を設定すると、逆側は依然として `null` になる可能性があるため、**双方向の関係を同期**する必要があります。そのために `this` を使用して、現在のオブジェクトを参照する関係を設定します。

### 2. **`this` を使用する基準**
双方向リレーションで関連オブジェクトを追加する際には、以下の基準で `this` を使用して関係を設定します。

1. **双方向リレーションの同期**: 双方向リレーションでは、一方のオブジェクトに関連するオブジェクトを設定する際、逆方向のオブジェクトにも現在のオブジェクト (`this`) を参照するように設定する必要があります。
   
2. **便利メソッドの使用**: 双方向リレーションの同期を明確に管理するために、**便利メソッド**を使用します。このメソッドは、2つのオブジェクトの関係を一度に同期するために役立ちます。

### 例: `Order` と `OrderItem` の双方向リレーション

#### 1. `Order` エンティティ

```java
@Entity
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> orderItems = new ArrayList<>();

    // 便利メソッドを使ってOrderItemとの関係を同期
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.setOrder(this);  // OrderItemでもOrderを参照するように設定
    }

    public void removeOrderItem(OrderItem orderItem) {
        orderItems.remove(orderItem);
        orderItem.setOrder(null);  // 関係を切るときもthisで設定
    }

    // Getters and Setters
}
```

#### 2. `OrderItem` エンティティ

```java
@Entity
public class OrderItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "order_id")
    private Order order;

    // Orderを設定するときthisを使って双方向リレーションを同期
    public void setOrder(Order order) {
        this.order = order;
    }

    // Getters and Setters
}
```

### 3. 双方向リレーションを設定する際の注意事項

- **両方の側で関係を設定する必要がある**: 例えば、`Order` に `OrderItem` を追加するときは、必ず `OrderItem` の `setOrder()` を呼び出して、逆方向も同期する必要があります。これを行わないと、JPAはデータベース上の関係を正しく認識できない可能性があります。
- **便利メソッド**: `addOrderItem()` のようなメソッドを作成し、双方向リレーションを一度に同期することが便利です。

### 4. 単方向リレーションの場合
- 単方向リレーションでは、`this` で相互参照する必要はありません。単方向の場合、一方のオブジェクトだけが参照されるため、双方向リレーションのように相互に同期する必要はありません。

### 例: 単方向リレーション

```java
@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany
    private List<Order> orders = new ArrayList<>();

    // Getters and Setters
}
```

この場合、`Order` は `Customer` を参照しないため、`this` を使用した同期は必要ありません。

### まとめ
- **永続性の伝播（CascadeType）**：親エンティティ（`PurchaseOrder`）と子エンティティ（`PurchaseOrderItem`）間に `CascadeType.ALL` を設定することで、親エンティティが保存または更新されると、子エンティティも一緒に処理されます。
- **リレーションの設定**：子エンティティは `ManyToOne` で親エンティティを参照し、親エンティティは `OneToMany` で子エンティティのリストを管理します。
- **在庫管理**：`InventoryItem` のようなエンティティは永続性の伝播を使用せず、個別に管理できます。

- **双方向リレーション**では、`this` を使用して**相互参照**を設定する必要があります。これにより、両方のエンティティが一貫性を持って接続されます。
- **便利メソッド**を作成して、双方向リレーションを一度に同期することをお勧めします。
- **単方向リレーション**では、相互参照がないため `this` を使用する必要はありません。