// 영속성 전이 예제 - 엔티티 정의
@Entity
public class Parent {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "parent")
private List<Child> children = new ArrayList<Child>();
...
}
@Entity
public class Child {
@Id @GeneratedValue
private Long id;
@ManyToOne
private Parent parent;
...
}
위와 같은 부모와 자식 엔티티가 있다고 하면, 부모 한명에 자식 두명을 저장할경우, 아래 코드와 같이 부모 한명과 자식 2명을 저장해야 하므로, 3번의 persist( ) 호출이 일어나야 한다.
// 영속성 전이를 설정하지 않았을때 연관관계 있는 엔티티들의 저장 예제
// 부모 저장
Parent parent = new Parent();
em.persist(parent);
// 1번 자식 저장
Child child1 = new Child();
child1.setParent(parent); // 자식 -> 부모 연관관계 설정
parent.getChildren().add(child1); // 부모 -> 자식
em.persist(child1);
// 2번 자식 저장
Child child2 = new Child();
child2.setParent(parent); // 자식 -> 부모 연관관계 설정
parent.getChildren().add(child2); // 부모 -> 자식
em.persist(child2);
💡 JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 한다.
위의 예제 코드에서는 부모 엔티티를 영속 상태로 만든 뒤, 자식 엔티티도 각각 영속 상태로 만들어야 한다.
영속성 전이를 사용하면 부모만 영속 상태로 만들면 연관된 자식까지 한 번에 영속 상태로 만들 수 있다.
// 영속성 전이 활성화 예제
@Entity
public class Parent {
...
@OneToMany(mappedBy = "parent", cascad = CascadeType.PESIST)
private List<Child> children = new ArrayList<Child>();
...
}
위의 예제처럼 영속성 전이를 활성화 할 경우, 부모 한명에 연관된 자식 두명을 저장하는 코드가 꽤나 줄어든다.
// 영속성 전이 활성화 후, 저장 예제
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
child1.setParent(parent); // 연관관계 추가
child2.setParent(parent); // 연관관계 추가
parent.getChildren().add(child1);
parent.getChildren().add(child2);
// 부모와 연관된 자식들 저장
em.persist(parent);
💡 주의 : 영속성 전이는 연관관계를 매핑하는 것과는 아무 관련이 없다.
영속성 전이는 엔티티를 삭제할 때도 사용할 수 있다.
// 영속성 전이를 사용하지 않는 삭제 예제
Parent findParent = em.find(Parent.class, 1L);
Child findChild1 = em.find(Child.class, 1L);
Child findChild2 = em.find(Child.class, 2L);
em.remove(findChild1);
em.remove(findChild2);
em.remove(findParent);
영속성 전이를 사용하지 않으면 저장과 같이, em.remove( )를 세번 호출해야한다.
// 영속성 전이를 사용하는 삭제 예제
Parent findParent = em.find(Parent.class, 1L);
em.remove(findParent);
💡 삭제 순서는 외래 키 제약조건을 고려해, 자식을 먼저 삭제하고 부모를 삭제한다.
위의 예제코드를 영속성 전이를 사용하지 않고 실행한다면, 부모 엔티티만 삭제된다.
하지만 데이터베이스의 외래 키 제약조건으로 인해, 예외가 발생한다.
// CASCADE 옵션을 정의한 ENUM 예제
public enum CascadeType {
ALL, // 모두 적용
PERSIST, // 영속
MERGE, // 병합
REMOVE, // 삭제
REFRESH, // REFRESH
DETACH // DETACH
}
cascade = {CascadeType.PESIST , CasadeType.REMOVE}
// 고아 객체 제거 기능 설정
@Entity
public class Parent {
@Id @GeneratedValue
private long id;
@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> children = new ArrayList<Chlid>();
}
// 고아객체 삭제 예제
Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0); // 자식 엔티티를 컬렉션에서 제거한다.
💡 고아 객체 제거 기능은 영속성 컨텍스트를 플러시할 때 적용된다.
💡 영속성 전이는 DDD(도메인 주도 개발)의 Aggregate Root 개념을 구현할 때 사용하면 편리하다.