개발

JPA 즉시 로딩 JPA Repository를 쓰면서 실수 안 할 수 있는가?

박카스마시며코딩 2024. 4. 6. 23:02

회사에서 백엔드를 JPA를 통해 개발하였고, 동료분들은 즉시 로딩과 지연 로딩을 자유자재로 사용하고 계셨다. 

문제를 발견한 것은 JPA Repository이다. 

다음은 엔티티의 코드이다. (설명에 필요한 부분들로 최소한으로 작성하였습니다)

Product를 조회할 때 Eager를 통해 variant도 가져오도록 하였다.

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class Product {
    @Id
    @Column(name = "product_id")
    private Long id;

    @OneToMany(mappedBy = "product" , fetch = FetchType.EAGER)
    private List<Variant> variants;

    private ZonedDateTime deletedAt;
}

 

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class Variant {
    @Id
    @Column(name = "variant_id")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "product_id", foreignKey = @ForeignKey(name = "none"))
    private Product product;

    private ZonedDateTime deletedAt;
}

 

 

@Repository
public interface ProductRepository extends JpaRepository<Product,Long> {
    Optional<Product> findByIdAndDeletedAtIsNull(long id);
    List<Product> findByDeletedAtIsNull();
}

당시 soft-delete를 하고 있어, 조회할 때 deleted_at이 null인 값만 가져와야했기에 위와 비슷한 형태의 메서드가 만들어졌다. 

과연 해당 메서드를 사용했을 때 product를 조회하면서 variant를 join 해서 한번에 가져올까?

전혀 그렇지 않다. join해서 가져오지 않고 product를 가져오고 각각의 product에 대해 variant를 가져와 N+1 문제까지 발생할 수 있다.

 

공식문서를 조금 더 찾아봐야겠지만, 추측으로는 JPQL로 변경되면서 EAGER가 잘 동작하지 않는 것으로 보인다.
(혹시 공식문서에서 관련 내용을 찾으신다면, 댓글로 알려주세요)

당시 이 문제는 batch옵션을 넣어서 N+1문제는 해결하였고, 한번에 조회해야하는 경우, QueryDSL로 fetchJoin으로 가져오록 해결하였습니다. 

 

아쉽게 회사 사정상 해당 프로젝트가 중단되었지만, 진행되었다면, Product는 4개 테이블과 연관관계를 맺고 있었고, 한번에 20개 정도 조회한다고 했으니, 쿼리의 개수를
Procut 조회 쿼리 (1) + 4개 연관관계 불러오는 쿼리 (4 * 20) = 81개 쿼리를 Product 조회 쿼리 (1) + 4개의 연관관계 불러오는 쿼리 (4) = 5개로 줄일 수 있었을 것이다.

'개발' 카테고리의 다른 글

TDD를 알고리즘 문제 풀이에 적용시켜보자  (0) 2022.05.10
TDD START!  (0) 2022.05.03
DTO의 사용 범위에 대한 생각  (0) 2022.04.12