JPA공부) join처리할때 N+1이란? 해결방법은?

J의 괴발 공부·2023년 11월 27일
0

java

목록 보기
13/13

테이블끼리 컬럼을 통해 조인을 하는 방법은 sql문장으로는 쉬운편이라고 생각한다.. 그런데 이문장을 jpa로 처리하는 방식이 어렵다 ㅠㅠ
어노테이션을 통해서 하는데, 이게 참...여기저기 입력을 해야해서 귀찮으면서도 편하달까?
그냥 sql문장으로 처리하면 되는데.. 그럼 그순간에만 편해지고 나중에 프로젝트 사이즈가 커지면 에러가 터지는거죠???????!!!!!!!!!!
JPA는 초반에 이것저것 설정하는게 너무~~~많은듯? 어쩌면 MyBatis 편할지도..?
여튼!!!!!!!! 까먹지 말고 기록을 남기자 !!!

자, sql문장으로 테이블끼리 컬럼을 끼고 조인을 하는 문장을 생각해보면서 하면 이해가 쉽다!

우선 조인할 컬럼에 패치타임을 설정을 하는데 보통 관계가 1:다 가 많을것이다, 그럼 OneToMany / ManyToOne 으로 설정을 하고 패치타임을 설정하면됨.

내 개인 프로젝트의 예시를 보고 설명

인터넷 쇼핑몰이 주제이며 상품=프로덕트에 카테고리가 필요함
그래서 카테고리 컬림인 카테고리id가 프로덕트에 들어가야함
즉, 한 카테고리에 여러 제품이 속할 수 있음 (1:N)
하나의 제품은 하나의 카테고리에 속함 (N:1)
이것을 sql 문장으로 처리하면

CREATE TABLE Category (    
    category_id BIGINT NOT NULL,
    PRIMARY KEY (category_id)
);

CREATE TABLE Product ( 
    category_id BIGINT,
    FOREIGN KEY (category_id) REFERENCES Category(category_id)
);

카테고리가 pk가 되고, 그 pk가 프로덕트의 fk로 들어가여 join이 됨
이것을 JPA 로 매핑을 하면,

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name="category_id", nullable = false)
    private Category category;

 @OneToMany
 @JoinColumn(name = "category_id")
    private List<Product> products;

여기서 패치타입이란????????????

<패치타입에 대한 설명>

FetchType.EAGER = 연관관계 엔티티를 join 을 통해서 한꺼번에 조회해온다.
-> Product 랑 Category 를 한꺼번에 가져옴

FetchType.LAZY = 연관관계 엔티티를 실제로 사용하는 시점에 select 쿼리를 날려서 데이터를 가져온다.
-> 1. Product 만 가져오고
-> 2. Category 를 사용하는 시점에 쿼리를 날려서 데이터를 가져온다.

FetchType.EAGER을 하면 한꺼번에 다 가져오기때문에,, 어쩌면 부담될지도? 그래서 LAZY로 하는데.. 그렇게 되면 문제가 발생하게 된다...

!!!문제!!!
Lazy Option 을 주게 되면, 사용하는 시점마다 쿼리가 날라가기때문에 (N+1) 성능적인 영향이 있다.

테스트용!!

@RequiredArgsConstructor
@Service
public class CategoryService {

    private final CategoryRepository categoryRepository;
  
    public void findAllCategory() {
        var categories = categoryRepository.findAll();

        // iter -> enchanced for 문을 작성해줌
        System.out.println(categories.size());
        for (Category category : categories) {
            var productIds = category.getProducts().stream()
                    .map(it -> it.getId())
                    .collect(Collectors.toList());
        }

        System.out.println("11111111");
    }
}


테스트로 카테고리 서비스를 만들고, 조회를 해보았다!
처음 카테고리에 대한 조회가 나오고

그밑에 카테고리가 속한 프로덕트에 대한 갯수만큼 계속 출력이 됨...

만약 갯수가 많더라면..? 100개 그 이상이라면..? 으악 생각하기도 싫다!!
해결책은 급한대로,
-> 해결: sql의 join문을 사용하지말고 in절을 사용하자!!!
그래서, yml에 default_batch_fetch_size: 1000 <-추가하자,해석: in절 1000가능함

그랬더니 아래와 같이 출력이 나왔다!

짠! in절로 카테고리 조회랑 속한 프로덕트만 나옴.. 후.....
sql을 더 공부해야겠다.. 뭔가 프로젝트를 하면할수록 DB와 관련된 게 너무 중요하다고 느껴지고 그래서 sql이 더 알아야겠다는 생각이 든다!!

profile
괴발자가 될것인가, 개발자가 될것인가?

0개의 댓글