Cascade
cascade를 사용하면 엔티티를 persist할 때 연관된 엔티티도 같이 persist 된다.
Parent.class
@Entity
@Getter @Setter
public class Parent {
@Id @GeneratedValue
private Long id;
private String name;
// CascadeType.ALL하면 Parent를 persist할 때 리스트 내부에 있는 녀석들도 모두 persist됨
@OneToMany(mappedBy = "parent",cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();
// 연관관계 편의 메소드
void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
Child.class
@Entity
@Getter @Setter
public class Child {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Parent parent;
}
JpaMain.class
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent); //parent만 persist 하여도 child1, child2도 persist된다.
tx.commit();
} catch (Exception e) {
tx.rollback(); //오류 발생 시 롤백
} finally {
//종료
em.close();
}
emf.close();
}
}
결과
Hibernate:
/* insert hellojpa.Parent
*/ insert
into
Parent
(name, id)
values
(?, ?)
Hibernate:
/* insert hellojpa.Child
*/ insert
into
Child
(name, PARENT_ID, id)
values
(?, ?, ?)
Hibernate:
/* insert hellojpa.Child
*/ insert
into
Child
(name, PARENT_ID, id)
values
(?, ?, ?)
Cascade같은 경우 한 부모에 여러 child가 있는 경우에 주로 많이 쓴다. 주의해야 할 점은 Child를 Parent라는 하나의 엔티티만이 관리(연관)할 때 사용하여야 한다.
예를 들어서 Child 엔티티가 Parent1 엔티티와 Parent2 엔티티 두개에 연관이 되어있는 경우 Child는 연관된 엔티티에서 Cascade 설정을 해서는 안된다. 연관된 엔티티가 하나일 때만 Cascade를 사용한다.
Cascade 종류
- ALL : 모두 적용
- PERSIST : 연관된 엔티티가 persist될 때 같이 영속됨
- REMOVE : 연관된 엔티티가 remove될 때 같이 삭제됨
고아 객체
고아 객체는 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 의미한다.
고아 객체 제거는 고아객체가 자동으로 삭제되는 것이다.
Parent.class
@Entity
@Getter @Setter
public class Parent { // Parent와 Child는 일대다 양방향 매핑
@Id @GeneratedValue
private Long id;
private String name;
// orphanRemoval = true 로 설정하면 고아객체 자동 삭제 설정이 되어, childList에서 제거된 Child 엔티티는 CHILD 테이블에서 자동으로 삭제된다.
@OneToMany(mappedBy = "parent", orphanRemoval = false)
private List<Child> childList = new ArrayList<>();
// 연관관계 편의 메소드
void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
JpaMain.class
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Parent parent = new Parent();
Child child1 = new Child();
Child child2 = new Child();
child1.setName("1");
child2.setName("2");
parent.addChild(child1);
parent.addChild(child2);
// CascadeType.ALL: parent만 persist 하여도 child1, child2도 persist된다.
em.persist(parent);
em.persist(child1);
em.persist(child2);
em.flush();
em.clear();
Parent findParent = em.find(Parent.class, parent.getId());
// childList의 0번째 인덱스 요소가 childList에서 제거됨.
// orphanRemoval = true이므로 리스트에서 제거된 엔티티(= 0번째 child)는 child 테이블에서도 삭제된다.
findParent.getChildList().remove(0);
tx.commit();
} catch (Exception e) {
tx.rollback(); // 오류 발생 시 롤백
e.printStackTrace(); // 에러 내용 출력
} finally {
//종료
em.close();
}
emf.close();
}
}
JpaMain 실행결과 Child가 제거됨
Hibernate:
/* delete hellojpa.Child */ delete
from
Child
where
id=?
DB의 모습을 보면 다음과 같다.
1) @OneToMany(orphanRemoval = false)인 경우 (디폴트)
childList에서 제거가 되었어도 여전히 CHILD 테이블에선 남아있는 모습.
2)@OneToMany(orphanRemoval = True)인 경우 (고아 객체 삭제 설정)
childList에서 제거된 1번 Child 엔티티는 CHILD 테이블에서도 삭제가 된 모습.
고아 객체 사용시 주의할 점
엔티티가 테이블에서 제거된다는 점은 딱봐도 위험하다. 그러므로 몇가지 주의할 점이 존재한다.
- 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아객체로 보고 삭제하는 기능
- 참조하는 곳이 하나일 때 사용해야한다!
- 특정 엔티티가 개인 소유할 때만 사용
- @OneToMany, @OneToOne만 가능 : 참조하는 쪽이 하나인 경우만 사용
개념적으로 부모를 제거하면 자식은 고아가된다. 따라서 고아 객체 제거기능을 활성화하면, 부모를 제거할 때 자식도 함께 제거된다. 이것은 CascadeType.REMOVE처럼 동작한다.
em.remove(findParent); // findParent 제거하면 연관된 chlid도 다 제거됨.
Hibernate:
/* delete hellojpa.Child */ delete
from
Child
where
id=?
Hibernate:
/* delete hellojpa.Child */ delete
from
Child
where
id=?
Hibernate:
/* delete hellojpa.Parent */ delete
from
Parent
where
id=?
'Spring > JPA' 카테고리의 다른 글
JPA- 값 타입과 불변 객체 (0) | 2023.03.23 |
---|---|
JPA- 임베디드 타입 (0) | 2023.03.22 |
JPA- 즉시 로딩, 지연 로딩 (0) | 2023.03.16 |
JPA- 프록시 (0) | 2023.03.16 |
JPA- @MappedSuperClass (0) | 2023.03.12 |