JPA Fetch Join과 페이징을 함께 사용할 때 주의점
●JPA Fetch Join과 페이징을 함께 사용할 때 주의점
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "product", cascade = CascadeType.PERSIST)
private List categories = new ArrayList();
// ... 중략
}
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
class ProductCategory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Product product;
@ManyToOne(fetch = FetchType.LAZY)
private Category category;
}
interface ProductJpaRepository extends JpaRepository {
@Query("""
select p
from Product p
join fetch p.categories pc
join fetch pc.category c
order by p.id desc
""")
Slice findProductWithSlice(Pageable pageable);
}
●왜 OOM이 발생할 수 있을까?
firstResult/maxResults specified with collection fetch; applying in memory
select
p.id,
pc.product_id,
pc.id,
c.id,
c.name
from
product p
join
product_category pc
on p.id = pc.product_id
join
category c
on c.id = pc.category_id
order by p.id desc
●이 문제는 어떻게 해결할수 있을까?
Slice result = productJpaRepository.findProductWithSlice(pageRequest);
result.forEach(product -> System.out.println(product.getCategories())); // N + 1
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@BatchSize(size = 10)
@OneToMany(mappedBy = "product", cascade = CascadeType.PERSIST)
private List categories = new ArrayList();
// ... 중략
}
select
pc.product_id,
pc.id,
pc.category_id
from
product_category pc
where
pc.product_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)