영속성 전이(CASCADE)
아래 코드들은 영속성 전이가 적용되지 않은 부모 엔티티와 자식 엔티티, 그리고 이를 활용한 비지니스 메소드들이 구현된 main 클래스이다.
부모 엔티티
@Entity @Getter @Setter
public class Parent {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "parent")
private List<Child> childList = new ArrayList<>();
public void addChild(Child child){
childList.add(child);
child.setParent(this);
}
}
Child 엔티티와 OneToMany를 어노테이션을 통해 연관되어있다.
자식 엔티티
@Entity @Getter @Setter
public class Child {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name="parent_id")
private Parent parent;
}
main
public static void main(String args[]){
//JPA는 항상 팩토리가 있어야 하는데 Web 서버가 올라오는 시점에 db 당 하나만 생성된다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); //persistence.xml에서 설정정보 읽어옴
//고객의 요청이와 DB작업을 해야할때마다 계쏙 썻다가 버렸다가 반복을 한다. - 절대로 여러 스레드가 공유해서는 안된다. 장애 발생함
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); // JPA의 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
try{
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
tx.commit(); // 안해주면 dB에 반영 안됨
}catch(Exception e){
tx.rollback();
}finally {
em.close();
}
emf.close(); //WAS가 내려갈때 닫아줘야함
}
parent를 persist하면 parent만 persist되고, child들은 persist되지 않는다.
만약 이를 가능하게 해주고 싶다면 각 childe 객체들을 일일이 persist해주거나
또는 연관 관계 매핑시에 cascade 속성을 통해 자동 전이되도록 할 수 있다.
@Entity @Getter @Setter
public class Parent {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();
...(생략)...
}
위처럼 cascade 속성을 추가하면 이젠 parent 객체만 persist해도 자동으로 child 객체들이 persist된다.
CASCADE 옵션 종류
1) ALL : 모두 적용
2) PERSIST : 영속
3) REMOVER : 삭제
4) MERGE : 병합
5) REFRESH : REFRESH
6) DETACH : DETACH
- 보통 ALL 또는 PERSIST 둘이 사용된다. 저장할 때만 연쇄반응을 일으키고 싶으면 PERSIST를,
다른 모든 케이스에서 연쇄를 적용시키고 싶으면 ALL을 적용하기 때문이다.
CASCADE는 자식 엔테티가 하나의 부모 엔티티와만 연관이 있을 경우에 사용한다.
예를 들어 특정 게시판의 게시글들은 해당 게시판과만 연관이 있으므로 CASCADE 적용이 바람직하다.
단, 자식 엔티티가 부모 엔티티말고도 다른 엔티티와 연관 관계가 있을 경우에는 CASCADE를 적용하면 안됨
(단일 엔티티에 완전히 종속적(단일 소유자)일 때 LIFE CYCLE이 거의 유사하기 때문에 CASCADE를 사용한다.)
고아 객체
: 부모 엔티티와 연관관계가 끊어진 자식 엔티티로 자동 삭제된다.
- 연관 관계 속성에 orphanRemoval을 true로 설정해주면 자식 객체들을 예비 고아 객체로 만들 수 있다.
@Entity @Getter @Setter
public class Parent {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();
...(생략)...
}
public static void main(String args[]){
//JPA는 항상 팩토리가 있어야 하는데 Web 서버가 올라오는 시점에 db 당 하나만 생성된다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); //persistence.xml에서 설정정보 읽어옴
//고객의 요청이와 DB작업을 해야할때마다 계쏙 썻다가 버렸다가 반복을 한다. - 절대로 여러 스레드가 공유해서는 안된다. 장애 발생함
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); // JPA의 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
try{
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
em.flush();
em.close();
Parent findParent = em.find(Parent.class,parent.getId());
findParent.getChildList().remove(0);
tx.commit(); // 안해주면 dB에 반영 안됨
}catch(Exception e){
tx.rollback();
}finally {
em.close();
}
emf.close(); //WAS가 내려갈때 닫아줘야함
}
위 코드를 실행시키면 자식 객체가 부모 객체와 연관이 끊어지는 순간, 해당 자식 객체는 삭제된다.
주의점
- 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능
- 이도 cascade와 마찬가지로 참조하는 곳이 하나일 때 사용해야한다.(특정 엔티티가 개인 소유인 경우)
- @OneToOne, @OneToMany에서만 사용 가능한 옵션
- 개념적으로 부모를 제거하면 자식 객체가 고아가 되면서 함께 삭제된다. 즉, CascadeType.REMOVE처럼 동작한다.
CascadeType.ALL + orphanRemovel=true를 통한 자식 객체 생명주기 관리
- 두 옵션을 모두 활성화하면 부모 엔티티를 통해 자식의 생명 주기를 관리할 수 있음
'JPA' 카테고리의 다른 글
[JPA] 값 타입과 불변 객체 (0) | 2023.07.21 |
---|---|
[JPA] 임베디드 타입 (0) | 2023.07.21 |
[JPA] 프록시와 즉시/지연 로딩 (0) | 2023.07.17 |
[JPA] 연관 관계 매핑 (0) | 2023.07.17 |
[JPA] 필드와 컬럼 매핑 (0) | 2023.07.15 |