인프런 커뮤니티 질문&답변

김싸피님의 프로필 이미지
김싸피

작성한 질문수

실전! Querydsl

2개 이상의 OneToMany 관계에서 fetch join 문제

작성

·

1.7K

0

안녕하세요 영한님!

좋은 강의 너무너무 잘 듣고 있습니다!

다름이 아니라 JPA와 Querydsl을 공부하던 중 궁금한 점이 생겨 이렇게 질문을 드립니다.

OneToMany 관계에서는 N+1 문제를 해결하기 위해 fetch join을 사용해 자식 엔티티도 모두 함께 가져오는 것으로 배웠습니다.

만약 아래처럼 하나의 엔티티와 자식 엔티티, 자식 엔티티의 자식 엔티티가 모두 OneToMany 관계인 경우 최상위 엔티티를 가져올 때 한꺼번에 모든 자식 엔티티를 가져올 수 있는 방법이 있는지 여쭤보고 싶습니다..!

@Entity
public class Forest{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "forest_id")
    private Long id;

    @OneToMany(mappedBy = "forest")
    private List<Tree> trees= new ArrayList<>();
}

@Entity
public class Tree{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "tree_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "forest_id")
    private Forest forest;

    @OneToMany(mappedBy = "tree",  cascade = CascadeType.ALL)
    private List<Leaf> leaves= new ArrayList<>();
}

@Entity
public class Leaf{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "leaf_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "Tree_id")
    private Tree tree;
}

위 코드를 기준으로 제가 원하는 것은 모든 Forest을 가져올 때, Forest의 자식인 Tree와 Tree의 자식인 Leaf를 함께 가져오는 것인데,

처음엔 단순히 fetch join을 두번 사용하면 될 것이라고 생각했습니다.

따라서 코드를 다음과 같이 작성했습니다.

public List<Forest> findAllFetch() {
        return queryFactory
                .selectFrom(forest)
                .leftJoin(forest.trees, tree).fetchJoin()
                .leftJoin(tree.leaves).fetchJoin()
                .fetch();
}

하지만 위 코드처럼 두개 이상의 OneToMany 관계에서 fetch join을 여러번 사용하면 MultipleBagFetchException이 발생했습니다. 

찾아보니 JPQL에서는 xToMany에서는 일반적으로 fetch join은 한번만 허용된다고 하더라구요...

따라서 제가 생각한 해결책은 위 과정을  2번의 쿼리로 나누어 실행시키는 방법이었습니다.


public List<Forest> findAllFetch() {
	List<Forest> result = queryFactory
                .selectFrom(forest).distinct()
                .leftJoin(forest.trees).fetchJoin()
                .fetch();

	List<Long> selectedIds = result.stream()
                .map(Forest::getId)
                .collect(Collectors.toList());

	queryFactory
                .selectFrom(tree).distinct()
                .leftJoin(tree.leaves).fetchJoin()
                .where(tree.forest.id.in(selectedIds ))
                .fetch();
    return result ;
}

혹시 위 코드보다 더 나은 방법이 있는지 아니면 한번의 쿼리로 모든 자식 엔티티를 받아올 수 있는 방법이 있는지 궁금합니다.

답변 1

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 김싸피님

한줄로 답변을 드릴 수 있으면 좋겠지만, 이 부분을 이해하려면 여러가지 성능 최적화 개념을 이해하셔야 합니다.

관련해서 실전! 스프링 부트와 JPA 활용2편 강의에서 상황별로 다양한 최적화 방법을 자세히 설명드립니다.

감사합니다.

김싸피님의 프로필 이미지
김싸피
질문자

답변 감사합니다!!

따흐흑....ㅠㅠ  완강하고 얼른 활용 2편을 수강해야 할것 같네요.

김영한님의 프로필 이미지
김영한
지식공유자

네 화이팅^^!

김싸피님의 프로필 이미지
김싸피

작성한 질문수

질문하기