JPA의 영속성 전이와 고아 객체에 대해 알아보자.
영속성 전이는 왜 필요할까?
특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 수 있다. 이 작업을 간편하게 하기 위해 영속성 전이를 사용한다.
Parent.java
@Setter
@Getter
@Entity
public class Parent {
@Id
@GeneratedValue
@Column(name = "PARENT_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "parent")
private List<Child> children = new ArrayList<Child>();
}
Child.java
@Getter
@Setter
@Entity
public class Child {
@Id @GeneratedValue
@Column(name = "CHILD_ID")
private Long id;
private String name;
@ManyToOne
private Parent parent;
}
Parent(부모) 엔티티와 Child(자식) 엔티티가 있고, 부모는 여러명의 자식을 가진다고 가정해보자.
// 부모 저장
Parent parent = new Parent();
parent.setName("임종수");
entityManager.persist(parent);
// 1번 자식 저장
Child child1 = new Child();
child1.setName("임준영");
child1.setParent(parent); // 자식 -> 부모 연관관계 설정
parent.getChildren().add(child1); // 부모 -> 자식
entityManager.persist(child1);
// 2번 자식 저장
Child child2 = new Child();
child2.setName("임주리");
child2.setParent(parent); // 자식 -> 부모 연관관계 설정
parent.getChildren().add(child2); // 부모 -> 자식
entityManager.persist(child2);
부모와 두 명의 자식을 저장하기 위해서는 모두 영속 상태여야 한다.
위의 코드는 부모와 두 명의 자식을 영속 상태로 만들기 위해 entityManager.persist를 세 번 호출해야 한다.
이럴 때 영속성 전이를 사용하면 부모 엔티티만 영속 상태로 만들면 연관된 자식까지 한번에 영속 상태로 만들 수 있다.
@Setter
@Getter
@Entity
public class Parent {
@Id @GeneratedValue
@Column(name = "PARENT_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
private List<Child> children = new ArrayList<Child>();
}
cascade 옵션을 PERSIST로 설정한다. 해당 설정을 통해 부모를 영속상태로 만들게 되면 children에 속해있는 자식들도 영속 상태로 만들 수 있다.
// 1번 자식 저장
Child child1 = new Child();
// 2번 자식 저장
Child child2 = new Child();
Parent parent = new Parent();
parent.setName("부모부모");
child1.setName("자식1");
child2.setName("자식2");
child1.setParent(parent); // 자식 -> 부모 연관관계 설정
child2.setParent(parent); // 자식 -> 부모 연관관계 설정
parent.getChildren().add(child1); // 부모 -> 자식
parent.getChildren().add(child2); // 부모 -> 자식
// 부모 저장
entityManager.persist(parent);
영속성 전이는 저장 외에도 사용할 수 있다.
public enum CascadeType{
ALL, // 모두적용
PERSIST, // 영속
MERGE, // 병합
REMOVE, // 삭제
REFRESH, // REFRESH
DETACH // DETACH
}
참고로 CascadeType.PERSIST, CascadeType.REMOVE는 em.persist(), em.remove()를 실행 할 때 바로 전이가 발생하지 않고 플러시를 호출 할 때 전이가 발생합니다.
JPA는 부모 엔티티와 연관관계가 끊어진 자식 엔티티인 고아 객체를 자동으로 삭제하는 기능을 제공한다.
즉, 부모 엔티티의 컬렉션에서 자식 엔티티의 참조를 제거하면 자식 엔티티가 자동으로 삭제되도록 할 수 있다.
@Setter
@Getter
@Entity
public class Parent {
@Id @GeneratedValue
@Column(name = "PARENT_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> children = new ArrayList<Child>();
}
orphanRemovel = true 설정을 하게 되면
아래의 코드에서 부모 엔티티의 컬렉션에 0번째 자식을 삭제하게 되면 자식 엔티티의 Delete 쿼리가 발생한다.
Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0);
//자식 엔티티를 컬렉션에서 제거
CascadeType.ALL + orphanRemoval = true
위 처럼 두 옵션을 활성화 하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다
//자식을 저장하려면 부모에 등록만 하면 됨.
Parent parent = em.find(Parent.class , parentId);
parent.addChild(child); // 이건 편의메소드로 따로 만들어줘야함
//자식을 삭제하려면 부모에서 제거하면 됨.
Parent parent = em.find(Parent.class , parentId);
parent.getChildren().remove(removeObject);