[TIL] JPA - 지연 로딩과 즉시 로딩

phdljr·2023년 11월 15일
0

TIL

목록 보기
31/70
post-custom-banner

지연 로딩과 즉시 로딩

  • JPA 에서는 연관관계에서 필요한 엔티티만 가져올 수 있는 FetchType 기능을 제공함
  • @ManyToOne 은 기본 전략이 즉시 로딩이다.(EAGER)
    • 즉시 연관 엔티티를 가져온다.
  • @OneToMany 은 기본 전략이 지연 로딩이다.(LAZY)
    • 연관 엔티티가 사용될 때 가져온다.
    • 지연 로딩된 객체의 정보를 조회하기 위해선, 반드시 해당 객체가 영속성 컨텍스트에 존재해야 한다.
      • 즉, 트랜잭션 환경이 적용돼야 한다.
          	@Test
            @DisplayName("아보카도 피자 조회 - 즉시 로딩")
            void test1() {
                Food food = foodRepository.findById(2L).orElseThrow(NullPointerException::new);
        
                System.out.println("food.getName() = " + food.getName());
                System.out.println("food.getPrice() = " + food.getPrice());
        
                System.out.println("아보카도 피자를 주문한 회원 정보 조회");
                System.out.println("food.getUser().getName() = " + food.getUser().getName());
            }
      • 지연 로딩인데 트랜잭션 환경이 아니라면 예외가 발생한다.
            @Test
            @DisplayName("Robbie 고객 조회 실패 - 지연 로딩")
            void test3() {
                User user = userRepository.findByName("Robbie");
                System.out.println("user.getName() = " + user.getName());
        
                System.out.println("Robbie가 주문한 음식 이름 조회");
        				// 트랜잭션 황경이 아니라서, food가 영속성 컨텍스트에 존재하지 않음
                for (Food food : user.getFoodList()) {
                    System.out.println(food.getName());
                }
            }

언제 사용해야 하는가?

지연 로딩

  • 연관 엔티티에 직접 접근을 하는 순간, 쿼리 요청을 보내 실제 데이터를 가져옴
    • 접근하기 전까진, 프록시 객체로 래핑된 상태(거의 빈 껍데기 수준)
  • 연관 관계를 맺은 엔티티에서, 원하는 정보만 효율적으로 가져오기 위해 사용
    • 접근할 필요가 없는 연관 엔티티는 굳이 쿼리 요청을 보낼 필요가 없음

즉시 로딩

  • 연관 엔티티가 사용될 예정이라면, 쿼리 요청을 두 번 보내는 것보단 한 번에 가져오기 위해 사용

프록시와 즉시 로딩 주의할 점

1. 즉시 로딩은 연관 엔티티가 많을 수록 부담이 크다.

  • @ManyToOne 연관 엔티티가 5개라면?
    • join이 5개가 발생한다. -> 예외가 발생될 가능성이 매우 높다.

2. 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.

  • EntityManager.find()는 PK를 정해놓고 DB에서 가져오기 떄문에 JPA 내부에서 최적화를 할 수 있다.
  • 하지만, JPQL에선 입력 받은 query string이 그대로 SQL로 변환된다.
    • 예시) Member 엔티티에 Team 연관 엔티티가 즉시 로딩으로 설정돼있다고 가정
    • select m from Member m -> 멤버 N명을 가져옴
    • 각 멤버마다 팀이 존재 -> 멤버당 Team 엔티티를 가져오는 쿼리를 날림
      • 즉, 멤버 수만큼 팀을 조회하는 쿼리가 N번 발생
      • 멤버 가져오는 쿼리 1번 + 각 멤버의 팀 조회하는 쿼리 N번 => N+1 문제
  • 해결 방법
    • 지연 로딩을 사용해서 필요할 때만 쿼리를 요청
    • fetch join을 활용
    • EntityGraph, 어노테이션, 배치 사이즈 설정 등의 방법도 존재

참조

https://ict-nroo.tistory.com/132

profile
난 Java도 좋고, 다른 것들도 좋아
post-custom-banner

0개의 댓글