Engineering Note

[JPA] 영속성 전이와 고아객체 제거 본문

Server/JPA ORM

[JPA] 영속성 전이와 고아객체 제거

Software Engineer Kim 2025. 9. 14. 13:21

영속성 전이, cascade

- 엔티티의 상태를 변경할 때 해당 엔티티와 연관된 엔티티의 상태 변화를 전파시키는 옵션

 

CASCADE 종류 설명

PERSIST 부모 엔티티가 영속화될 때 자식 엔티티도 영속화
MERGE 부모 엔티티가 병할될 때 자식 엔티티도 병합
REMOVE 부모 엔티티가 삭제될 때 연관된 자식 엔티티도 삭제
REFRESH 부모 엔티티가 refresh되면 연관된 자식 엔티티도 refresh
DETACH 부모 엔티티가 detach되면 연관된 자식 엔티티도 detach 상태로 변경
ALL 부모 엔티티의 영속성 상태 변화를 자식 엔티티에 모두 전이

 

 

Order Entity가 있고, OrderItem Entity가 있을 때 Order Entity가 OrderItem Entity를 참조하고 있을 때 Order Entity가 OrderItem의 부모 엔티티로 볼 수 있는데, 아래처럼 영속성 전파 설정을 할 수 있다.

 

@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)

 

 

실습 코드

 

Order Entity

package com.shop.entity;

import com.shop.contant.OrderStatus;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "orders")
@Getter
@Setter
public class Order extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "order_id")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;

    private LocalDateTime orderDate; //주문일

    @Enumerated(EnumType.STRING)
    private OrderStatus orderStatus; //주문상태

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

 

 

OrderItem Entity

package com.shop.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Entity
@Table(name = "order_item")
@Getter @Setter
public class OrderItem extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "order_item_id")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "item_id")
    private Item item;

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

    private int orderPrice;

    private int count;
}

 

cascade = ALL 옵션 덕분에, Order가 영속성 컨텍스트에 저장되면 연관된 OrderItem들도 자동으로 영속화된다.

 

 

 

 

고아 객체와 고아 객체 제거

- 부모엔티티와 연관 관계가 끊어진 자식 엔티티를 고아 객체라고 한다. 

 

    Order order = new Order();

    for(int i = 0; i < 3; i++) {
        Item item = this.createItem();
        itemRepository.save(item);
        OrderItem orderItem = new OrderItem();
        orderItem.setItem(item);
        orderItem.setCount(10);
        orderItem.setOrderPrice(item.getPrice());
        orderItem.setOrder(order);
        order.getOrderItems().add(orderItem);

    }
    
    order.getOrderItems().remove(0);//OrderItem객체는 고아객체” → “OrderItem 객체는 이제 고아 객체가 되어 DELETE 대상이 됨

 

그런데 이미 DB에 저장되어 있는 Order와 OrderItem 이라면 자식객체를 제거한다고 해서 삭제 쿼리문이 실행되는건 아니다. 

 

public Order createOrder() {
    Order order = new Order();

    for (int i = 0; i < 3; i++) {
        Item item = createItem();
        itemRepository.save(item);
        OrderItem orderItem = new OrderItem();
        orderItem.setItem(item);
        orderItem.setCount(10);
        orderItem.setOrderPrice(item.getPrice());
        orderItem.setOrder(order);
        order.getOrderItems().add(orderItem);
    }

    Member member = new Member();
    memberRepository.save(member);

    order.setMember(member);
    orderRepository.save(order);
    return order;
}

@Test
@DisplayName("고아객체 제거 테스트")
public void orphanRemovalTest() {
    Order order = this.createOrder();
    order.getOrderItems().remove(0);
    log.info("======= flush() 호출 =======");
    em.flush();
}

 

 

고아 객체 제거 설정이 되어 있지 않다면 지금 코드를 실행하였을 때 delete 쿼리문이 실행되지 않는다. orphanRemoval 옵션이 없으면 이미 DB에 저장된 자식 엔티티를 컬렉션에서 제거해도 DELETE 쿼리는 실행되지 않는다. 하지만 아래 처럼 고아객체 제거 옵션은 true로 설정하고 위의 코드를 실행하면 고아객체를 삭제하는 쿼리문이 실행된다. 

 

@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)

 

 

실행 결과

 

Comments