0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JPAの Persistence Cascade

Last updated at Posted at 2024-10-03

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

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

永続性の伝播(Cascade)に関する主な原則

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

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

サンプルコード

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

1. PurchaseOrder エンティティ

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

@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 エンティティ

PurchaseOrderItemPurchaseOrder および InventoryItem と関連しており、PurchaseOrder との関係で ManyToOne として設定されています。

@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 設定は不要です。

@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 は別途保存または管理できます。

@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 を使用する理由

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

2. this を使用する基準

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

  1. 双方向リレーションの同期: 双方向リレーションでは、一方のオブジェクトに関連するオブジェクトを設定する際、逆方向のオブジェクトにも現在のオブジェクト (this) を参照するように設定する必要があります。

  2. 便利メソッドの使用: 双方向リレーションの同期を明確に管理するために、便利メソッドを使用します。このメソッドは、2つのオブジェクトの関係を一度に同期するために役立ちます。

例: OrderOrderItem の双方向リレーション

1. Order エンティティ

@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 エンティティ

@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. 双方向リレーションを設定する際の注意事項

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

4. 単方向リレーションの場合

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

例: 単方向リレーション

@Entity
public class Customer {

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

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

    // Getters and Setters
}

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

まとめ

  • 永続性の伝播(CascadeType):親エンティティ(PurchaseOrder)と子エンティティ(PurchaseOrderItem)間に CascadeType.ALL を設定することで、親エンティティが保存または更新されると、子エンティティも一緒に処理されます。

  • リレーションの設定:子エンティティは ManyToOne で親エンティティを参照し、親エンティティは OneToMany で子エンティティのリストを管理します。

  • 在庫管理InventoryItem のようなエンティティは永続性の伝播を使用せず、個別に管理できます。

  • 双方向リレーションでは、this を使用して相互参照を設定する必要があります。これにより、両方のエンティティが一貫性を持って接続されます。

  • 便利メソッドを作成して、双方向リレーションを一度に同期することをお勧めします。

  • 単方向リレーションでは、相互参照がないため this を使用する必要はありません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?