현의 개발 블로그

[프록시와 연관관계 관리] 영속성 전이, 고아객체 본문

스프링부트 이론/JPA

[프록시와 연관관계 관리] 영속성 전이, 고아객체

hyun2371 2023. 5. 28. 22:06

영속성 전이 (CASCADE)

영속성 전이는 특정 엔티티를 영속 상태로 만들고, 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때 사용한다.

아래의 예시를 살펴보자.
Parent와 Child 객체를 만들고 양방향 연관관계를 매핑하였다.

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

	private String name;

	@OneToMany(mappedBy = "parent")
	private List<Child> childList = new ArrayList<>();

	private void addChild(Child child){
		childList.add(child);
		child.setParent(this);
	}
}
@Entity
public class Child {
	@Id @GeneratedValue
	private Long id;

	private String name;

	@ManyToOne
	@JoinColumn(name = "parent_id')
	private Parent parent;
}
Child child1 = new Child();
Child child2 = new Child();

Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);

em.persist(parent);
em.persist(child1);
em.persist(child2);

 

Parent 객체를 만들고 2개의 Child 객체를 만들어 Parent 참조에 넣었다.
세 객체를 모두 영속화하려면 em.persist()를 매번 호출해줘야 한다.
 
매번 persist()를 작성해야 하는게 번거롭다.
parent가 영속화될 때 child도 같이 영속화된다면 편리할 것이다.
이때 영속성 전이(Cascade)를 활용할 수 있다.
 
영속성 전이를 사용하기 위해서는 cascade를 추가해줘야 한다.

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();

그러면 Parent가 persist 할 때 컬렉션 요소 childList도 연쇄적으로 persist 된다.

 
 

CASCADE 옵션

  • ALL: 모두 적용
  • PERSIST: 영속
  • REMOVE: 삭제
  • MERGE: 병합
  • REFRESH
  • DETACH: 비영속

총 6개의 옵션이 있지만 ALL 또는 PERSIST를 주로 사용한다.
ALL은 위의 해당하는 모든 영속성이 전이되는 경우이다.
PERSIST는 엔티티가 영속화할 때만 연쇄적으로 영속화하는 옵션이다.

 
 

CASCADE를 어디에 사용할까

일대다 연관관계에서 다(多)에 연관관계 주인이 존재한다.
연관관계 주인의 반대편인 일(一)쪽에 cascade를 사용한다.
@OneToMany를 매핑한 엔티티에 사용한다고 할 수 있다.

 

CASCADE를 언제 사용할까

1) 두 엔티티의 라이프 사이클이 동일하거나 비슷해야 한다.
2) 연관관계 반대편 엔티티(child)가 연관관계 주인 엔티티(parent)에 종속적이어야 한다.

 

단일 엔티티에 종속적일 때는 likecycle이 동일하니까 사용해도 된다.
 
예를 들어, 게시판 내에서만 첨부 파일을 사용하면 Cascade를 쓸 수 있다.
하지만, 첨부 파일을 다른 엔티티에서도 관리한다면 쓸 수 없다.

 
 
 

고아객체 (OrphanRemoval)

orphanRemoval = true로 설정하면
부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제한다.
참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 판단해 삭제하는 것이다.

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();

 

Child child1 = new Child();
Child child2 = new Child();

Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);

em.persist(parent);

em.flush();
em.clear();

Parent findParent = em.find(Parent.class, parent.getId());
findParent.getChildList().remove(0);

 

Parent 객체가 관리하는 childList에서 제거가 되면, DB에서도 제거된다.

ID가 2인 child 삭제됨

부모를 제거하면 자식은 고아가 된다.
따라서 고아 객체 기능을 활성화하면, 부모를 제거할 때 자식도 함께 제거된다.

em.remove(findParent);

Parent가 지워지면 childList 삭제되므로 자식까지 delete됨

고아 객체 주의할 점

참조가 제거된 엔티티는 '다른 곳에서 참조하지 않는 고아 객체'로 보고 삭제하는 기능이다.
 
따라서 참조하는 곳이 하나일 때 사용해야 한다.
여러 곳에서 참조하는데 한 곳에서 참조가 끊긴다고 삭제하면 문제가  생기기 때문이다.
특정 엔티티가 해당 엔티티를 소유할 때 사용해야 위험하지 않다.
 
같은 맥락으로 @OneToOne, @OneToMany일 때만 사용 가능하다.

 
 

영속성 전이 + 고아객체 생명주기

스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거한다.
 
CasecadeType.ALL, orphanRemoval = true
두 옵션을 모두 활성화 하면, 부모 엔티티를 통해 자식 엔티티의 생명주기 관리할 수 있다.

 

CascadeType.REMOVE VS 고아 객체

두 옵션 모두 부모 엔티티가 삭제되면, 자식 엔티티도 삭제된다는 점은 동일하다.

 

CascadeType.REMOVE
부모 엔티티가 자식 엔티티와의 관계를 제거해도 자식 엔티티는 삭제되지 않고 남아있다.
orphanRemoval = true
부모 엔티티가 자식 엔티티와의 관계를 제거하면 자식 엔티티는 고아가 되어 삭제된다.
 
orphanRemoval = true vs CascadeType.REMOVE

 
 


Reference

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 
 

Comments