2024. 8. 27. 17:41ㆍJPA
2024/08/27
※ 지연 로딩에 대해 알아보자.
▶ 지연 로딩과 즉시 로딩
● 음식 테이블과 고객 테이블이 N : 1 양방향 관계라 가정해보자.
➡️ FetchTypeTest
package com.sparta.jpaadvance.fetch;
import com.sparta.jpaadvance.entity.Food;
import com.sparta.jpaadvance.entity.User;
import com.sparta.jpaadvance.repository.FoodRepository;
import com.sparta.jpaadvance.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
public class FetchTypeTest {
@Autowired
UserRepository userRepository;
@Autowired
FoodRepository foodRepository;
@Test
@Transactional
@Rollback(value = false)
void init() {
List<User> userList = new ArrayList<>();
User user1 = new User();
user1.setName("Robbie");
userList.add(user1);
User user2 = new User();
user2.setName("Robbert");
userList.add(user2);
userRepository.saveAll(userList);
List<Food> foodList = new ArrayList<>();
Food food1 = new Food();
food1.setName("고구마 피자");
food1.setPrice(30000);
food1.setUser(user1); // 외래 키(연관 관계) 설정
foodList.add(food1);
Food food2 = new Food();
food2.setName("아보카도 피자");
food2.setPrice(50000);
food2.setUser(user1); // 외래 키(연관 관계) 설정
foodList.add(food2);
Food food3 = new Food();
food3.setName("후라이드 치킨");
food3.setPrice(15000);
food3.setUser(user1); // 외래 키(연관 관계) 설정
foodList.add(food3);
Food food4 = new Food();
food4.setName("후라이드 치킨");
food4.setPrice(15000);
food4.setUser(user2); // 외래 키(연관 관계) 설정
foodList.add(food4);
Food food5 = new Food();
food5.setName("고구마 피자");
food5.setPrice(30000);
food5.setUser(user2); // 외래 키(연관 관계) 설정
foodList.add(food5);
foodRepository.saveAll(foodList);
}
}
➡️ 아보카도 피자 조회
@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());
}
● “아보카도 피자”의 가격을 조회하려고 했을 뿐인데 자동으로 JOIN 문을 사용하여
연관관계가 설정되어있는 고객 테이블의 정보도 가져오고 있다.
● JPA는 연관관계가 설정된 Entity의 정보를 바로 가져올지, 필요할 때 가져올지 정할 수 있다.
○ 즉, 가져오는 방법을 정하게되는데 JPA에서는 Fetch Type이라 부른다.
○ Fetch Type의 종류에는 2가지가 있는데 하나는 LAZY , 다른 하나는 EAGER 다.
○ LAZY 는 지연 로딩 으로 필요한 시점에 정보를 가져온다.
○ EAGER 는 즉시 로딩 으로 이름의 뜻처럼 조회할 때 연관된 모든 Entity의 정보를
즉시 가져온다.
● 기본적으로 @OneToMany 애너테이션은 Fetch Type의 default 값이 LAZY 로 지정되어있고
반대로 @ManyToOne 애너테이션은 EAGER 로 되어있다.
● 다른 연관관계 애너테이션들도 default 값이 있는데 이를 구분하는 방법이 있다.
○ 애너테이션 이름에서 뒤쪽에 Many가 붙어있으면 설정된 해당 필드가
Java 컬렉션 타입일 것이다.
▪ 즉, 해당 Entity의 정보가 여러 개 들어있을 수 있다는 것을 의미한다.
▪ 따라서 효율적으로 정보를 조회하기 위해 지연 로딩 이 default로 설정되어있다.
○ 반대로 이름 뒤쪽이 One일 경우 해당 Entity 정보가 한 개만 들어오기 때문에
즉시 정보를 가져와도 무리가 없어 즉시 로딩 이 default로 설정되어있다.
➡️ Robbie 고객 조회
@Test
@Transactional
@DisplayName("Robbie 고객 조회")
void test2() {
User user = userRepository.findByName("Robbie");
System.out.println("user.getName() = " + user.getName());
System.out.println("Robbie가 주문한 음식 이름 조회");
for (Food food : user.getFoodList()) {
System.out.println(food.getName());
}
}
● 이번에는 Robbie 고객을 조회한 후 Robbie 고객이 주문한 음식들의 이름을 조회했다.
● @OneToMany 즉, default가 지연 로딩 으로 설정되어있기 때문에 우선 고객을 조회한 후
● user.getFoodList() 호출 즉, 주문한 음식의 정보가 필요한 시점에 음식 테이블에
해당 고객 Entity의 식별자 값을 사용하여 Select SQL이 수행되었다.
▶ 영속성 컨텍스트와 지연 로딩
● 지난 포스팅에서 영속성 컨텍스트의 기능에 대해 알아보았다.
○ 1차 캐시
○ 쓰기 지연 저장소
○ 변경 감지
● 지연 로딩도 마찬가지로 영속성 컨텍스트의 기능 중 하나다.
○ 따라서 지연 로딩된 Entity의 정보를 조회하려고 할 때는
반드시 영속성 컨텍스트가 존재해야한다.
○ ‘영속성 컨텍스트가 존재해야한다’라는 의미는
결국 ‘트랜잭션이 적용되어있어야 한다’라는 의미와 동일하다.
➡️ Robbie 고객 조회 실패
@Test
@DisplayName("Robbie 고객 조회 실패")
void test3() {
User user = userRepository.findByName("Robbie");
System.out.println("user.getName() = " + user.getName());
System.out.println("Robbie가 주문한 음식 이름 조회");
for (Food food : user.getFoodList()) {
System.out.println(food.getName());
}
}
○ ‘Robbie 고객 조회 실패’ 테스트 코드를 확인해보면 @Transactional이 test3() 메서드에
설정되어있지 않다.
○ 트랜잭션이 적용되지 않았기 때문에 지연 로딩 된 음식 Entity 정보들을 user.getFoodList()
즉, 필요한 시점에 조회하려고 하자 오류가 발생했다.
○ 따라서 지연 로딩 된 정보를 조회하려고 할 때는 반드시 트랜잭션이 적용되어 영속성 컨텍스트가
존재하는지를 확인해야한다.
※ 위 이미지들은 스파르타코딩클럽에 저작권이 있으므로 무단 도용 금지 및 상업 목적으로 사용할 수 없습니다.
'JPA' 카테고리의 다른 글
JPA - 고아 Entity 삭제 (0) | 2024.08.29 |
---|