Search
Duplicate
📒

[JPA 기본] 08-2. 즉시/지연 로딩, 영속성 전이(CASCADE) ⭐

상태
완료
수업
JPA
주제
JPA
4 more properties
참고

즉시 로딩과 지연 로딩

NOTE
회원 엔티티를 조회할 때, 연관된 팀 엔티티도 같이 조회되는 것이 좋을까? 아니면 회원 엔티티만 조회해두고 팀 엔티티는 실제 사용하는 시점에 조회하는 것이 좋을까?
이는 정확한 정답이 없고, 상황에 따라 달리질 수 있다.
이러한 방식을 설정하는 것이 즉시 로딩지연 로딩이다.

즉시 로딩

NOTE
즉시 로딩을 사용하면 Member를 조회하면 Team한번에 조인해서 둘다 조회한다.
@Entity public class Member{ // ... @ManyToOne(fetch = FetchType.EAGER) //즉시로딩 사용 @JoinColumn(name="TEAM_ID") private Team team; // ... } //... Member m = em.find(Member.class, member1.getId()); //Member 객체 반환 System.out.println("m = "+ m.getTeam().getClass()); //Team 객체 반환
Java
복사
fetch = FetchType.EAGER
즉시 로딩 EAGER를 사용해서 한번에 조회한다.

지연 로딩

NOTE
지연 로딩을 사용하면 Member를 조회하면 Team실제 사용시점에 조회한다.
@Entity public class Member{ //... @ManyToOne(fetch = FetchType.LAZY) //지연로딩 사용 @JoinColumn(name="TEAM_ID") private Team team; //... } //... Member m = em.find(Member.class, member1.getId()); //Member 객체 반환 System.out.println("m = "+ m.getTeam().getClass()); // Team$HibernateProxy객체 반환 m.getTeam().getName() // team을 실제로 사용하는 시점에서 db조회 엔티티 반환
Java
복사
fetch = FetchType.LAZY
지연 로딩 LAZY를 사용해서 프록시로 조회하고, 실제 사용시 실제 엔티티를 가져온다.

프록시와 즉시로딩 주의 (N+1 문제)

NOTE
N+1문제는 연관 관계가 설정된 엔티티를 조회할 경우, 연관된 엔티티 데이터 개수(n)만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어온다.
@Entity public class Member{ // 즉시 로딩을 사용하는 경우 @OneToMany(fetch = FetchType.EAGER) @JoinColumn(name="TEAM_ID") private Team team; // ... }
Java
복사
Member(1) - Team(N)의 관계라고 가정한다.
즉시 로딩을 실행하는 경우 Meber의 쿼리를 수행한 이후 Meber에 있는 Team의 데이터를 가져오기 위한 쿼리를 수행하게 된다.
이렇게 되는 경우 Meber가 속한 Team의 개수만큼 조회 쿼리가 추가적으로 발생하고 DB에 접근하는 횟수가 늘어나면서 성능저하의 문제가 발생한다.

JPA 기본 fetch 전략

@ManyToOne, @OneToOne기본이 즉시 로딩전부 LAZY로 설정
@OneToMany, @ManyToMany기본이 지연 로딩

영속성 전이: CASCADE

NOTE
객체를 저장하거나 삭제할 때 연관된 객체도 함께 저장하거나 삭제할 수 있는데 이것을 영속성 전이라 한다.
JPACASCADE 옵션으로 영속성 전이를 제공한다.
@OneToOne, @OneToMany에서만 사용가능

영속성 전이 - 저장

NOTE
객체 관계

영속성 전이 사용안함

// 부모 저장 Parent parent = new Parent(); em.persist(parent) ; // 1번 자식 저장 Child child1 = new Child(); child1.setParent(parent); //자식 -> 부모 연관관계 설정 parent.getChildren().add(childl) ; //부모 -> 자식 em.persist(childl); // 2번 자식 저장 Child child2 = new Child(); child2.setParent(parent); //자식 -> 부모 연관관계 설정 parent.getChildren().add(child2); //부모 -> 자식 em.persist(child2);
Java
복사
parent와 child 요소를 모두 각각 persit 해줘야함.

영속성 전이 사용

@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST) private List<Child> children = new ArrayList<Child>();
Java
복사
cascade PERSIST로 설정
Child child1 = new Child(); Child child2 = new Child(); Parent parent = new Parent(); childl.setParent(parent) ; //연관관계 추가 child2.setParent(parent) ; //연관관계 추가 parent.getChildren().add(child1); parent.getChildren().add(child2); //부모저장, 연관된자식들저장 em.persist(parent);
Java
복사
연관객체의 persit 한번으로 모두 저장된다.

CASCADE 종류

NOTE
public enum CascadeType { ALL, //모두 적용 PERSIST, //영속 MERGE, //병합 REMOVE, //삭제 REFRESH, //REFRESH DETACH //DETACH }
Java
복사
parent와 child 요소를 모두 각각 persit 해줘야함.
CascadeType.PERSIST, CascadeType,REMOVEem.persist(), em.remoce()를 실행할 때 바로 전이가 발생하지 않고, 플러시를 호출할 때 전이가 발생한다.

고아 객체

NOTE
@OneToMany(mappedBy = "parent", orphanRemoval = true) private List<Child> children = new ArrayList<Child>(); Parent parent1 = em.find(Parent.class, id); parent1.getChildren().remove(0); //자식 엔티티를 컬렉션에서 제거
Java
복사
JPA는 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기능을 제공하는데, 이를 고아 객체 제거라고 한다.
부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거자식 엔티티가 자동으로 삭제된다.
참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제를 위한 기능이므로, @OneToOne, @OneToMany에서만 사용할 수 있다.
[ 참고]
개념적으로 부모를 제거하면 자식은 고아가된다. 따라서 고아 객체 제거 기능을 활성화 하면, 부모를 제거할 때 자식도 함께 제거된다. (CascadeType.REMOVE처럼 동작한다.)
Parent객체를 지우게 되면 Parent가 소유하고있는 ChildList에 속한 엔티티들이 전부 같이 삭제된다.

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

NOTE
CascadeType.ALL+ orphanRemoval = true ⇒ 부모 엔티티를 통해 생명주기를 관리한다.
일반적으로 엔티티는 EntityManager.persist()를 통해 영속화되고, EntityManager.remove()를 통해 제거된다.
이것은 엔티티 스스로 생명주기를 관리한다는 의미이다.
그런데 두 옵션을 모두 활성화하면 부모 엔티티를 통해 자식 생명주기를 관리할 수 있다.
이는 DDDAggregate Root 개념을 구현할 때 편리하다.