CascadeType.REMOVE vs. orphanRemoval = true 차이?
CascadeType.REMOVE | orphanRemoval = true | |
부모 엔티티 삭제 | 자식 엔티티도 삭제됨 | 부모 삭제시 자식 엔티티도 삭제됨 (부모가 자식의 삭제 생명주기 관리) |
부모-자식 연관관계 제거 | 단순 연관관계 제거만으로는 자식 엔티티는 삭제되지 아니함 |
자식 엔티티 = 고아 객체 취급 자동으로 DB에서 삭제됨 |
부모-자식 연관관계 변경 | 잘 변경됨 | 잘 변경됨 |
한편, CascadeType.PERSIST와 CascadeType.REMOVE의 차이?
- persist: 새로운 엔티티를 영속성 컨텍스트에 저장하여 DB에 추가.
- remove: 이미 존재하는 엔티티를 영속성 컨텍스트에서 제거하고 DB에서 삭제.
예시
// persist 예시
Order newOrder = new Order(); // 새로운 객체
newOrder.setDate(LocalDate.now());
entityManager.persist(newOrder); // DB에 저장
// remove 예시
Order existingOrder = entityManager.find(Order.class, 1L); // DB에서 조회
entityManager.remove(existingOrder); // DB에서 삭제
예시 상황
CascadeType.REMOVE
주문(Order)과 주문 상세(OrderDetail)
- Order 엔티티가 삭제되면 OrderDetail도 함께 삭제되어야 하는 경우
- Order는 여러 OrderDetail을 가질 수 있지만, Order가 삭제되면 관련된 OrderDetail도 삭제되어야 함.
-> CascadeType.REMOVE가 설정된 경우, Order 엔티티가 삭제되면 관련된 OrderDetail도 자동으로 삭제
더보기
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private LocalDateTime orderDate;
@OneToMany(mappedBy = "order", cascade = CascadeType.REMOVE)
private List<OrderDetail> orderDetails = new ArrayList<>();
}
@Entity
public class OrderDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private int quantity;
private BigDecimal price;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
}
orphanRemoval = true
회원(User)과 즐겨 찾기 레시피(UserFavoriteRecipe)
- User는 여러 UserFavoriteRecipe를 가질 수 있지만, 사용자가 특정 레시피를 즐겨찾기에서 제거하면 해당 레시피는 자동으로 삭제되어야 하는 경우
더보기
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserFavoriteRecipe> favoriteRecipes = new ArrayList<>();
}
@Entity
public class UserFavoriteRecipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@ManyToOne
@JoinColumn(name = "recipe_id")
private Recipe recipe;
}
간단한 판단 기준:
- 부모가 삭제될 때 자식도 삭제되어야 할 경우: CascadeType.REMOVE
- 자식이 부모와의 연관 관계를 끊었을 때 자식만 삭제되어야 할 경우: orphanRemoval = true
두 옵션을 동시에 사용할 수 있는 경우:
- 부모 삭제 시 자식도 삭제되고, 자식이 부모와의 관계를 끊었을 때도 자식이 삭제되어야 한다면 두 옵션을 함께 사용할 수 있음.
번외
CascadeType.ALL에 포함된 Cascade 옵션:
- CascadeType.PERSIST: 부모 엔티티가 저장될 때, 자식 엔티티도 자동으로 저장됩니다.
- CascadeType.MERGE: 부모 엔티티가 병합될 때, 자식 엔티티도 자동으로 병합됩니다.
- CascadeType.REMOVE: 부모 엔티티가 삭제될 때, 자식 엔티티도 자동으로 삭제됩니다.
- CascadeType.REFRESH: 부모 엔티티가 새로고침될 때, 자식 엔티티도 자동으로 새로고침됩니다.
- CascadeType.DETACH: 부모 엔티티가 분리될 때, 자식 엔티티도 자동으로 분리됩니다.