개발천재

[Spring Boot] JPA Cascade 완벽 가이드 본문

개발 준비/Spring Boot

[Spring Boot] JPA Cascade 완벽 가이드

세리블리 2025. 2. 27. 23:44
반응형

JPA의 Cascade(영속성 전이) 이해하기

JPA에서 영속성 전이(Cascade) 란, 부모 엔티티가 저장되거나 삭제될 때 연관된 자식 엔티티도 함께 처리되도록 하는 기능이다. 예를 들어, 부모를 데이터베이스에 저장하면 자식도 자동으로 저장되거나, 부모를 삭제하면 자식도 같이 삭제되도록 설정할 수 있다.

 

이를 통해 연관된 객체를 일일이 관리하지 않아도 되므로 코드가 간결해지고 유지보수가 쉬워진다는 장점이 있지만 부모를 삭제할 때 자식까지 예상치 않게 삭제될 수도 있으므로 신중하게 사용해야 한다.

@Entity
class Parent {
    @Id @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
    private List<Child> children = new ArrayList<>();
    
    public void addChild(Child child) {
        children.add(child);
        child.setParent(this);
    }
}

 

Cascade가 필요한 이유

관계형 데이터베이스에서 부모-자식 관계(예: @OneToMany, @ManyToOne)를 맺을 때, 부모 엔티티가 변경되면 자식 엔티티도 함께 변경되도록 만들기 위해 사용된다.

예를 들어, 부모 엔티티를 저장할 때 자식 엔티티도 자동으로 저장되도록 하려면 CascadeType.PERSIST를 설정하면 된다.

@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)

 

 

 


 

 

CascadeType 종류

ALL
모든 영속성 전이 (PERSIST, MERGE, REMOVE, REFRESH, DETACH 전부 적용)

cascade = CascadeType.ALL



PERSIST
부모 엔티티를 저장할 때 자식 엔티티도 함께 저장

cascade = CascadeType.PERSIST



MERGE
부모 엔티티를 병합할 때 자식 엔티티도 함께 병합

cascade = CascadeType.MERGE



REMOVE
부모 엔티티를 삭제하면 자식 엔티티도 함께 삭제

cascade = CascadeType.ALL



REFRESH
부모 엔티티를 다시 조회하면 자식 엔티티도 함께 새로고침

cascade = CascadeType.REMOVE



DETACH
부모 엔티티를 영속성 컨텍스트에서 분리할 때 자식 엔티티도 함께 분리

cascade = CascadeType.DETACH

 

 


 

 

Cascade 적용 예제

CascadeType.PERSIST
부모 엔티티를 저장할 때, 연관된 자식 엔티티도 함께 저장된다.

@Entity
class Parent {
    @Id @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
    private List<Child> children = new ArrayList<>();
    
    public void addChild(Child child) {
        children.add(child);
        child.setParent(this);
    }
}

@Entity
class Child {
    @Id @GeneratedValue
    private Long id;

    @ManyToOne
    private Parent parent;

    public void setParent(Parent parent) {
        this.parent = parent;
    }
}
Parent parent = new Parent();
Child child1 = new Child();
Child child2 = new Child();

parent.addChild(child1);
parent.addChild(child2);

entityManager.persist(parent); // parent를 저장하면 child1, child2도 자동 저장됨

 

 

 

CascadeType.REMOVE
부모 엔티티가 삭제될 때, 연관된 자식 엔티티도 자동으로 삭제된다.

부모 삭제 시 자식도 모두 삭제되므로, 실제 데이터가 모두 사라지는 문제가 발생할 수 있다.
orphanRemoval = true와 함께 사용하면 부모가 자식 리스트에서 제거할 때 자동으로 삭제된다.

@OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
private List<Child> children = new ArrayList<>();
entityManager.remove(parent);
// parent를 삭제하면 children 리스트의 모든 child도 삭제됨



 

아이돌 그룹 멤버와 엔터테이먼트 엔티티로 보는 예시

// Entertainment Entity
@Entity
@Data
public class Entertainment {
    @Id
    @Column(name = "e_id")
    private String id;
    private String name;

    @OneToMany(mappedBy = "entertainment",
            fetch = FetchType.EAGER,
            cascade = CascadeType.PERSIST)
    List<GirlGroup> girlGroupList = new ArrayList<>();
}

// GirlGroup Entity
@Entity
@Data
public class GirlGroup {
    @Id
    @Column(name = "g_id")
    private String id;
    private String name;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "e_id")
    Entertainment entertainment;

    @OneToMany(mappedBy = "girlGroup",
            fetch = FetchType.EAGER,
            cascade = CascadeType.PERSIST)
    List<IdolMember> idolMemberList = new ArrayList<>();
}

// IdolMember Entity
@Entity
@Data
public class IdolMember {
    @Id
    @Column(name = "i_id")
    private String id;
    private String name;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "g_id")
    private GirlGroup girlGroup;
}

 

 

Service

@Service
@Transactional
public class ExamService {
    @Autowired
    EntityManager em;

    public void initData() {
        IdolMember member_1 = new IdolMember();
        IdolMember member_2 = new IdolMember();
        IdolMember member_3 = new IdolMember();
        IdolMember member_4 = new IdolMember();

        member_1.setId("안유진");
        member_1.setName("유진");
        member_2.setId("장원영");
        member_2.setName("원영");
        member_3.setId("제니");
        member_3.setName("재니");
        member_4.setId("지수");
        member_4.setName("지수다");

        GirlGroup girlGroup_1 = new GirlGroup();
        GirlGroup girlGroup_2 = new GirlGroup();

        girlGroup_1.setId("ive");
        girlGroup_1.setName("아이브");
        girlGroup_2.setId("blackPink");
        girlGroup_2.setName("블핑");

        Entertainment entertainment_1 = new Entertainment();
        Entertainment entertainment_2 = new Entertainment();

        entertainment_1.setId("startship");
        entertainment_1.setName("스타쉽");
        entertainment_2.setId("YG");
        entertainment_2.setName("와이지");

        // 🔥 관계 설정 (FK 설정)
        member_1.setGirlGroup(girlGroup_1);  // 유진 → 아이브
        member_2.setGirlGroup(girlGroup_1);  // 원영 → 아이브
        member_3.setGirlGroup(girlGroup_2);  // 재니 → 블핑
        member_4.setGirlGroup(girlGroup_2);  // 지수 → 블핑

        girlGroup_1.setEntertainment(entertainment_1);  // 아이브 → 스타쉽
        girlGroup_2.setEntertainment(entertainment_2);  // 블핑 → YG

        // 🔥 @OneToMany 리스트에 추가
        girlGroup_1.getIdolMemberList().add(member_1);
        girlGroup_1.getIdolMemberList().add(member_2);
        girlGroup_2.getIdolMemberList().add(member_3);
        girlGroup_2.getIdolMemberList().add(member_4);

        entertainment_1.getGirlGroupList().add(girlGroup_1);
        entertainment_2.getGirlGroupList().add(girlGroup_2);

        // 🔥 저장
        em.persist(entertainment_1);
        em.persist(entertainment_2);
        em.persist(girlGroup_1);
        em.persist(girlGroup_2);
        em.persist(member_1);
        em.persist(member_2);
        em.persist(member_3);
        em.persist(member_4);
    }
}
반응형