[JPA] 영속성 전이, 고아 객체

Bam·2025년 5월 23일
0

Spring

목록 보기
64/73
post-thumbnail

영속성 전이

엔티티를 영속 상태로 만들 대상태로 만들 때 연관관계의 엔티티도 영속 상태로 만들어주는 기능을 영속성 전이 Transitive Persistence라고 합니다.

JPA에서는 영속성 전이 기능을 CASCADE 옵션을 통해 제공하고 있습니다.

CASCASE 옵션

다음과 같이 양방향 일대다 관계의 Customer - Order 엔티티가 있습니다.

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "c_id", nullable = false)
    private Long id;
    
    @OneToMany(mappedBy = "customer")
    private Order order;
    
    //생성자 및 getter
}
@Entity
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "o_id", nullable = false)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "c_id", nullable = false)
    private Customer customer;
    
    //생성자 및 getter
}

Customer 엔티티를 저장하기 위해서는 연관관계에 있는 Order 엔티티도 영속 상태로 만들어줘야합니다. 그래서 Customer만 저장하고 싶어도 Order를 영속 상태로 만들고 저장해야하는 과정이 반드시 필요합니다.

JPA는 이런 번거로운 과정을 줄이기 위해서 영속성 전이를 자동으로 해주는 CASCADE 옵션을 제공하고 있습니다.

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "c_id", nullable = false)
    private Long id;
    
    @OneToMany(mappedBy = "customer", cascade = CascadeType.PERSIST)	//CASCADE 옵션 활성화
    private Order order;
    
    //생성자 및 getter
}

위와같이 CASCADE 옵션을 활성화하면 Customer 엔티티를 저장할 때 자동으로 연관관계에 있는 Order 엔티티를 영속 상태로 만들고 저장해줍니다.

CascadeType

CASCADE 옵션에 사용할 수 있는 CascadeType은 다음과 같습니다.

CascadeType설명
ALL아래 모든 CascadeType을 포함
PERSIST부모 엔티티를 persist()할 때 연관된 자식 엔티티도 함께 영속화됨
MERGE부모 엔티티를 merge()할 때 자식 엔티티도 함께 병합됨
REMOVE부모 엔티티를 remove()할 때 자식 엔티티도 함께 삭제됨
REFRESH부모 엔티티를 refresh()할 때 자식 엔티티도 함께 새로고침됨
DETACH부모 엔티티를 detach()할 때 자식 엔티티도 함께 영속성 컨텍스트에서 분리됨

대부분의 상황에서 연관 엔티티를 함께 관리하기 때문에 CascadeType.ALL을 많이 사용한다고 합니다.


고아 객체 제거

고아 객체 Orphan Objects는 부모 엔티티와 연관관계가 끊어져 더 이상 참조되지 않는 자식 엔티티를 의미합니다.

JPA는 이런 고아 객체들을 자동으로 삭제(DB에서)해주는 고아 객체 제거 Orphan Removal 기능을 제공하고 있습니다. 고아 객체 제거는 부모 엔티티에서 자식 엔티티의 참조를 제거하면 자동으로 자식 엔티티가 삭제되게 됩니다.

orphanRemoval 옵션을 true로 설정하면 고아 객체 제거 기능이 켜지게 됩니다.

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "c_id", nullable = false)
    private Long id;
    
    @OneToMany(mappedBy = "customer", orphanRemoval = true)
    private Order order;
    
    //생성자 및 getter
}

고아 객체는 무조건 삭제해야한다?

부모 엔티티와의 연관관계가 끊어지면 고아 객체가 되지만 그렇다고 고아 객체가 반드시 쓸모없는 것은 아닙니다.

A 객체에서 B 객체를 참조하고 C 객체도 B 객체를 참조한다고 가정해봅시다.
A 객체의 참조가 사라졌다고 해서 B 객체를 같이 삭제하게 된다면 C 객체가 B 객체를 참조하는 로직들에서 오류가 발생하게 됩니다.

위와 같이 고아 객체라고해서 무조건 쓸모 없는 객체가 되는 것이 아니기 때문에 다음과 같은 상황에 대해서만 고아 객체를 삭제하도록 지시하는게 적절합니다.

  • 자식 객체가 오직 하나의 부모에만 속하고 다른 곳에서 참조되지 않는 경우
  • 설계 상 부모가 삭제되면 자식도 존재할 이유가 없는 경우

예를들어 게시글에 달린 댓글이 대표적인 고아 객체의 예시입니다. 게시글이 삭제됐을 때 해당 게시글의 댓글이 남아있을 이유가 있을까요?
이런 경우 댓글을 고아 객체 제거 기능을 이용해서 자동으로 제거되게 하는 것이 좋습니다.

위와 같은 경우들로 인해 고아 객체 제거 기능은 @OneToOne, @OneToMany에서만 사용할 수 있습니다.

0개의 댓글