Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@OneToMany에서의 N+1 이슈 해결 방법과 고찰 #8

Open
Sun26-Avrin opened this issue Feb 22, 2022 · 0 comments
Open

@OneToMany에서의 N+1 이슈 해결 방법과 고찰 #8

Sun26-Avrin opened this issue Feb 22, 2022 · 0 comments

Comments

@Sun26-Avrin
Copy link
Owner

Sun26-Avrin commented Feb 22, 2022

JPA를 처음 쓸때 접했던 이슈이자, 잘 알려진 유명한 이슈.

잘 알려진 해결 방법으로는 FetchJoin(=EntityGraph) 와 BatchSize가 있다.

잘 알려진 해결방법

  1. FetchJoin

outer join 으로 컬렉션(자식)을 모두 조회해오기 때문에, OOM 발생 우려

JPQL 을 작성해야하는 귀찮음과 getter 호출로 자식을 조회해오는 방식이 아니라, Repository 메서드로 자식을 가져오는 방식이라, 일관성이 깨진다 생각하여 필자는 잘 쓰지 않음.

  1. BatchSize

자가 참조 엔티티를 구현할 때 사용했던 어노테이션

JPQL 작성 필요 X + getter 호출로 인한 동작 O

getter 호출 시, 영속성에 존재하는 엔티티 레코드 갯수에서 최대, 지정된 size 만큼 부모의 식별자를 sql WHERE 절에 넣어 지정된 size 만큼 가져오는 형식 (보통 size 는 900~1000 사이가 효율적)

장점 : 따로 구현없이 @batchsize() 어노테이션 하나로 코드를 작성할 시간이 절약

단점 : 엔티티를 베이스로 동작하므로 Full Select 가 발생.
ex_) SELECT * FROM item;

필자가 선호하는 방법 (더 나은 방법 찾을 시, 계속 업데이트)

  1. BatchSize의 동작을 하드코딩으로 직접구현

장점 : 엔티티의 모든정보를 Select 할 필요없이 DTO Projection 으로 select문의 부피를 줄일 수 있음.
ex_) SELECT i.name, i.user_id FROM item i;

단점 : 직접구현해야되서 코딩시간 소요

@batchsize 사용과 직접 구현시 코드 비교

Item 엔티티는 @OneToMany로 List tags 를 가지고 있음

@batchsize 사용

    //@BatchSize Annotation 사용 시
    public List<ItemResWithTag> getAllItem(Long userId){
        //Select Item
        List<Item> itemList = itemRepository.findItemsByUserId(userId);
        
        //Select Tags
        //getter 호출 로, 영속성에 포함된 모든 item 의 List<Tag> 초기화
        System.out.println(folderList.get(0).getTags());
        
        //Item -> ItemResWithTag 로 변환
        /* ... 
           return ...;
        */
    }

BatchSize 기능 직접 구현

public List<ItemResWithTag> getAllItem(Long userId){
        //Select ItemRes
        List<ItemRes> itemList = itemRepository.findItemsByUserId(userId);
        
        //Select Tags
        // ItemPK로 Item과 Tag가 연결되어있다고 가정
        // 1. where 절에 담을 Item 의 PK값들 추출
        // 2. tagRepository 에서 in 절로 호출하여 모두 가져옴
        // 3. 가져온 tag 들을 ItemRes -> ItemResWithTag 로 변환하면서 연결 
        /*
           var ... = itemList.stream().map(ItemRes::getItemPk).collect(Colletors.toSet());
           var ... = tagRepoisotry.getTagsByItemPkIn(ItemPkList);
            .... return ...;
        */ 
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant