영속성 전이 : CASCADE
@Entity
public class Parent {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();
public void addChild(Child child){
childList.add(child);
child.setParent(this);
}
... getter and setter
}
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
public Parent getParent() {
return parent;
}
... getter and setter
}
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
tx.commit();
- cascade는 특정 엔티티를 저장하거나 삭제할때, 연관된 엔티티(=연관관계 매핑 @OneToMany, @ManyToOne 등을 통해 서로 연결되어 있는 엔티티)에도 같은 작업을 하도록 설정하는 기능이다. 즉, 영속성전이는 특정 엔티티를 영속 상태로 만들때 연관된 엔티티도 함께 영속 상태로 만들때 사용한다.
- 예를 들어 부모엔티티와 자식엔티티가 있다고 했을때, cascade는 일반적으로 부모 엔티티쪽에 적용한다. 그래야 부모엔티티를 저장할때 자식엔티티도 함께 저장된다.
- 즉, A를 저장할때 A와 연관된 B도 같이 저장하기 위해 사용한다. cascade는 일반적으로 부모 엔티티쪽에 적용한다고 했는데 이 말은 부모엔티티를 저장할때 자식엔티티도 같이 저장하기 위해 부모엔티티쪽에 적용하는것이다. 두 엔티티간에 부모엔티티나 자식엔티티 또는 ManyToOne, OneToMany, OneToOne상관없이 A를 저장할 때 연관된 B까지 같이 저장하고 싶다면, A 쪽에 CASCADE를 설정하면된다.
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL) private List<Child> childList = new ArrayList<>();
CACADE가 설정되어있는 곳이 컬렉션타입(List)이라면, List안에 있는 제네릭타입인 Child 엔티티들에게도 CASCADE가 적용되서 각각 em.persist()가 된다.
=> em.persist(parent)시 childList의 각 Child엔티티마다 em.persist(child)가 호출된다고 보면된다.
@ManyToOne(cascade = CascadeType.ALL) @JoinColumn(name = "parent_id") private Parent parent;
CACADE가 설정되어있는 곳이 엔티티타입이라면, Parent엔티티에도 CASCADE가 적용되서 em.persist()가 된다.
=> em.persist(child)시 em.persist(parent)가 호출된다고 보면된다.
- 참고로, 연관관계를 매핑하는것과는 관련이 없다. 즉, 연관관계를 맺는 과정에 영속성 전이는 필요없다. 예를 들어 1:N 연관관계를 맺을 때 CASCADE 속성 없이 @OneToMany만 있어도 연관관계를 맺을 수 있다. 연관관계를 맺는 과정에서 CASCADE는 아무런 역할을 하지않는다. 그래서 CASCADE를 하는 이유는 연관관계 매핑이 아닌 영속성 전이를 하기 위함이다.
- CascadeType.ALL : 모든 작업(저장, 수정, 삭제, detach 등등)이 전이됨.
- CascadeType.PERSIST : 특정 엔티티를 persist()할때 연관된 엔티티도 함께 persist()됨.
- CascadeType.REMOVE : 특정 엔티티를 remove()할때 연관된 엔티티도 함께 remove()됨.
orphanRemoval
@Entity
public class Parent {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();
public void addChild(Child child){
childList.add(child);
child.setParent(this);
}
... getter and setter
}
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
public Parent getParent() {
return parent;
}
... getter and setter
}
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);
tx.commit();
- 부모엔티티와 연관관계가 끊어진 자식엔티티를 자동으로 삭제한다. 즉, 부모 엔티티에서 자식 엔티티의 참조를 제거하면, 그 자식 엔티티를 자동으로 삭제한다.
- 부모엔티티와 연관관계가 끊어진것 = 부모 엔티티에서 자식 엔티티를 참조하지않는것.
- 그래서 DB에서 해당 자식엔티티를 DELETE한다.
- 연관관계 설정(연관관계 맺기) : member.setTeam()이나 team.getMembers().add(member)와 같은 메서드를 통해 값을 설정해주는것(=참조를 설정하는것)
- 연관관계 제거(연관관계 끊기) : member.setTeam(null)이나 team.getMembers().remove(0)와 같은 메서드를 통해 값을 설정해주는것(=참조를 제거하는것)
- @OneToMany와 @OneToOne에서만 사용할 수 있고, @ManyToOne에서는 사용할 수 없다.
- 참고로, em.remove(findParent); 를 통해 부모를 제거하면, 자식도 함께 제거된다.