JPA에서 연관 엔티티를 언제 조회할지 결정하는 옵션이다.
연관 엔티티를 실제로 사용할 때 조회한다.
@ManyToOne(fetch = FetchType.LAZY)
private Seller seller;
예를 들어 상품을 조회하면 상품만 가져오고,
Product product = productRepository.findById(1L).get();
이 시점에는 Seller를 조회하지 않는다.
하지만
product.getSeller().getName();
을 호출하는 순간 판매자를 조회하는 SQL이 실행된다.
장점
단점
부모 엔티티를 조회할 때 연관 엔티티도 함께 조회한다.
@ManyToOne(fetch = FetchType.EAGER)
private Seller seller;
상품을 조회하면 판매자를 사용하지 않아도 함께 조회한다.
장점
단점
@ManyToOne과@OneToOne의 기본 FetchType은EAGER이다.
실무에서는 대부분LAZY로 변경하여 사용한다.
LAZY 상태에서 부모 엔티티를 조회한 후 연관 엔티티를 반복해서 접근하면 추가 SQL이 계속 실행되는 문제이다.
List<Product> products = productRepository.findAll();
for (Product product : products) {
System.out.println(product.getSeller().getName());
}
실행되는 SQL
-- 1번
SELECT * FROM product;
-- 상품 개수만큼 반복
SELECT * FROM seller WHERE id = 1;
SELECT * FROM seller WHERE id = 2;
...
상품이 100개라면
➡️ 총 101번의 SQL이 실행된다.
이를 N+1 문제라고 한다.
Fetch Join은 연관 엔티티를 부모 엔티티와 함께 한 번에 조회하는 방법이다.
@Query("""
select p
from Product p
join fetch p.seller
""")
List<Product> findAllWithSeller();
실행되는 SQL
SELECT *
FROM product p
JOIN seller s
ON p.seller_id = s.id;
이후
product.getSeller().getName();
을 호출해도 추가 SQL이 발생하지 않는다.
즉,
| 구분 | 특징 |
|---|---|
| LAZY | 연관 엔티티를 실제 사용할 때 조회한다. |
| EAGER | 부모를 조회할 때 연관 엔티티도 함께 조회한다. |
| N+1 | 부모 조회 1번 + 연관 엔티티 조회 N번이 발생하는 성능 문제이다. |
| Fetch Join | 부모와 연관 엔티티를 한 번의 SQL로 조회하여 N+1 문제를 해결한다. |
@EntityGraph를 사용한다.