먹었으면 뇌를 쓰자

JPA 학습 내용을 정리한 로드맵 - 3.2 CASCADE 본문

Framework/JPA

JPA 학습 내용을 정리한 로드맵 - 3.2 CASCADE

뇌이비 2022. 12. 20. 16:16

 

1. JPA란 무엇인가
     1.1 기본 CRUD
     1.2 기초 지식

2. 연관관계
     2.1 다대일 연관관계 
     2.2 일대다 연관관계 

3. 상속
     3.1 상속관계
     3.2 CASCADE

4. SQL
     4.1 JPQL
     4.2 Querydsl

 

 

3.2 CASCADE

 

 

 

- Cascade 전이

- PERSIST

- REMOVE를 주의하라

 

 

 

 

 

<Cascade 전이>

 

Cascade는 '전이'라는 뜻이다. 

 

일대다(다대일) 연관관계를 맺은 두 엔티티가 있다.

프로그래머는 일 쪽의 상태가 변화하면, 다 쪽의 상태도 변화하도록 만들고 싶다.

 

예를 들어 사람Person, 주소Address 가 일대다 연관관계를 맺고 있다고 하자.

사람 엔티티가 존재해야 주소 엔티티가 의미를 가진다.

따라서 사람이 remove되면 연관된 주소들도 remove 되도록 cascade 할 수 있다.

 

 

Cascade(전이)는 어떤 엔티티의 상태가 변하면, 연관 엔티티의 상태도 똑같이 변하는 것이다.

 

 

 

 

<PERSIST>

 

JPA에서는 다양한 타입의 Cascade를 지원한다.

PERSIST, REMOVE, ... ALL 이 있다. 

 

가장 대표적인 PERSIST를 코드를 통해 살펴보자.

 

 

<Person.class>

@Entity
public class Person {
@Id @GeneratedValue(strategy= GeneratedValue.Identity)
private int id;
private String name;
@OneToMany(mappedBy = "person", cascade = CascadeType.PERSIST) 
// Person이 삭제되면, 연관 엔티티도 함께 삭제된다 (단방향적)
private List<Address> addresses = new ArrayList<Address>();
}
<Address.class>

@Entity
public class Address {
@Id @GeneratedValue(strategy= GeneratedValue.Identity)
private int id;
private String street;
private String city;
private int zipCode;
@ManyToOne(fetch = FetchType.LAZY)
private Person person;
}
<Main.class>

Address address1 = new Address();
address1.setCity("city1");
address1.setStreet("street1");
address1.setZipCode("zipCode1"); // 첫 번째 Address 

Address address2 = new Address();
address2.setCity("city1");
address2.setStreet("street1");
address2.setZipCode("zipCode1"); // 두 번째 Address

Person person = new Person();
person.setName("kim");
person.getAddresses().add(address1);
person.getAddresses().add(address2); // 양방향 연관관계 매핑

address1.setPerson(person);
address2.setPerson(person); // 양방향 연관관계 매핑

em.persist(person);
//em.persist(address1);
//em.persist(address2); 
*cascade 전에는 위와 같이 address1,2도 persist 해야 했지만 
*cascade 후에는 person만 persist해도 address1,2에 상태가 전이된다.

 

 

 

 

<REMOVE를 주의하라>

 

위의 코드에서 PERSIST 대신 REMOVE를 쓰면 

person을 remove했을 때 연관된 address1,2도 remove된다.

 

하지만 다대다 연관관계에서 REMOVE 전이는 지양해야 한다.

 

 

problem 1)

예를 들어 Author1이 Book 1,2를 집필했고

Author2가 Book 2,3을 집필했다고 해보자.

 

Author1를 삭제할 경우 전이를 통해 Book 1,2가 삭제된다.

이 과정에서 Book2를 공동집필한 Author2와 Book2의 연관관계도 끊어져버린다.

(끊어지면 안되겠다.)

 

 

 

problem 2)

위의 상황에서 CascadeType.REMOVE가 

Author뿐만이 아닌 Book에도 적용되어

양방향으로 전이가 된다고 가정해보자.

 

Book2가 삭제되면서

Author2와의 연관관계가 끊어지는 것을 넘어,

Author2 자체도 전이를 통해 삭제된다.

그러면 Author2가 집필한 Book3도 삭제된다.

 

Author 1,2 와 Book 1,2,3이 모두 삭제되는 초유의 사태가 벌어진다.

(절대 벌어지면 안되겠다.)

 

 

 

일대다 연관관계에서도 혹여나 싶은 마음이 있기에 REMOVE 전이는 지양하는 것이 좋다.

대신 사용할 수 있는 방법이 orphanRemoval이다.

 

일 쪽인 Book과 다 쪽인 Review가 있다.

Book이 삭제되면 딸린 Review들이 전부 삭제되어야 할 것이다.

이런 경우 orphanRemoval=true를 추가해주면 REMOVE 전이와 동일하게 기능한다.

 

 

REMOVE 전이는 '부모 엔티티 삭제 -> 자식 엔티티 삭제' 구조

orphanRemoval은 '부모 엔티티 삭제 -> 자식과의 매핑 끊김 -> 자식 엔티티 삭제' 구조이다.

 

 

<Book.class>

@Entity
public class Book {
@OneToMany(mappedBy = "book", orphanRemoval = true, cascade = CascadeType.PERSIST)
private List<Review> reviews = new ArrayList<Review>();
...
}

 

 

 

 

 

일대다(다대일) 연관관계는 orphanRemoval로 연관 엔티티 삭제하자.
다대다 연관관계는 삭제할 엔티티가 있다면 직접 remove 하자.
Comments