다시, N+1 문제가 발생하는 이유
JPA에서 연관관계 매핑이 되어있는 데이터들을 조회할 때 N+1 문제가 발생한다.
예를 들면, 한 사람이 작성한 게시글을 조회할 때 User 테이블과 Article 테이블을 조인한 형태의 쿼리문을 원했지만, User 테이블에서 조회하고 이후에 Article 테이블에서 해당 유저가 작성한 게시글을 N번 조회하는 현상이다.
왜 이런 일이 발생할까?
JPA가 내부적으로 사용하는 JPQL은 기본적으로 글로벌 Fetch 전략을 무시하고 JPQL만 갖고 SQL을 생성하기 때문에, JPA Repository로 find 시 실행하는 첫 쿼리에서 하위 엔티티까지 한 번에 가져오지 않고, 하위 엔티티를 사용할 때 추가로 조회한다.
JPA는 대상이 되는 엔티티에만 신경을 써서 연관관계까지는 파악하지 못해서 발생한다고 이해할 수 있다.
리팩토링을 하기 위해 다른 팀원이 짠 코드를 보다가 이상한 현상을 발견했다.
Post 엔티티에 Debate, User가 있는데 이들이 모두 EAGER로 되어있었다.
하지만 EAGER이라도 N+1 문제는 발생한다. 발생해야 한다.
그런데 N+1 문제가 발생하지 않았고, 콘솔에 sql을 확인해보니 세 테이블에 모두 조인되어 쿼리문이 나가고 있었다.
Post post = postRepository.findById(postId).get();
Debate debate = post.getDebate();
User user = post.getUser();
내가 지금까지 공부해온 것이 부정당하는 느낌....
그래서 찾아본 결과,
일반적으로 findById에 대한 메서드는 EntityManager에서 PK값을 찍어서 사용하기 때문에(?) JPA가 내부적으로 조인문을 사용하여 최적화해준다고 한다.
즉 findById()<EntityManager에서 em.find()>같은 경우 JPA가 내부적으로 조인문에 대한 쿼리를 만들어 반환하여 EAGER로딩으로는 N+1 문제가 발생하지 않는 것처럼 보인다.
하지만, 우리는 findById 메서드만 사용하지 않는다. 다양한 메서드를 사용한다. 이런 경우에는 자동으로 조인으로 최적화해주지 않는다.
따라서, LAZY 로딩을 사용하고 N+1 문제가 발생하지 않도록 하자.
'개발 > Spring' 카테고리의 다른 글
[리팩토링] Spring-data-JPA와 Querydsl을 함께 사용하며 (0) | 2023.10.01 |
---|---|
MySQL 외래키와 데드락 (1) | 2023.08.17 |
MySQL 비관적 락을 이용한 인원 제한 구현하기 (0) | 2023.08.17 |
[N+1 문제] 일반조인, fetch조인으로 OSIV, LAZY/EAGER, N+1문제 확실히 이해하기 (0) | 2023.08.16 |
JPA 프록시 개념/존재이유와 find(), getReferenceById() (0) | 2023.08.08 |
댓글