티스토리 뷰
반응형
N+1 쿼리 문제의 원인?
Spring Data JPA 에서 제공하는 Repository의 findAll(), findById()등과 같은 메소드를 사용하면 바로 DB에 SQL 쿼리를 날리는 것이 아닙니다.
JPQL 이라는 객체지향 쿼리 언어를 생성, 실행시킨 후 JPA는 이것을 분석해서 SQL을 생성, 실행하는 동작에서 N+1 쿼리 문제가 발생한다.
JPQL 입장에서는 LAZY로딩, EAGER 로딩과 같은 글로벌 패치 전략을 신경쓰지 않고 JPQL만 사용해서 SQL을 생성한다.
N+1 쿼리 문제는 언제 발생할까?
발생하는 경우는 다음과 같은 2가지 경우가 있다.
두개의 Entity가 1:N 관계를 가지며 JPQL로 객체를 조회할때
- EAGER 전략으로 데이터를 가지고 오는 경우
- LAZY 전략으로 데이터를 가져온 후 가져온 데이터에서 하위 Entity를 다시 조회하는 경우 (보통 이 경우가 가장 많이 발생 됨)
아래의 코드는 1:N 관계를 만들기 위해 하나의 앨범이 많은 노래를 가질 수 있도록 Entity를 생성하고 관계를 연결한 코드
Class.Album
@Entity
public class Album {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false)
private String albumTitle;
@Column(nullable = false)
private String locales;
// @OneToMany(mappedBy = "album", cascade = CascadeType.ALL, fetch = FetchType.EAGER) //
@OneToMany(mappedBy = "album", cascade = CascadeType.ALL, fetch = FetchType.LAZY) //
private List<Song> songs = new ArrayList<>();
}
Class.Song
@Entity
public class Song {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private int track;
@Column(nullable = false)
private int length;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "album_id")
private Album album;
}
Test Code
@Test
@Transactional // 테스팅에서 LAZY 전략시 필수
public void N1_쿼리테스트_2() throws Exception{
List<Album> albums = albumRepository.findAll();
for (Album album : albums) {
System.out.println(album.getSongs().size()); // Song에 접근 !
}
}
LAZY 방식
N+1 발생!
처음엔 Album 리스트만 조회 했다면 Album Entity 에서 하위 엔티티인 Song Entity로 접근 했기 때문에 LAZY 로딩이 일어나면서 N+1 문제 발생
N+1 문제 해결 방안
Fatch Join
@Query("select Distinct a from Album as a join fetch a.songs"
List<Album> findAllJoinFetch();
@Test
@Transactional // 테스팅에서 LAZY 전략시 사용해야 동작
public void FetchJoin_테스트() throws Exception{
List<Album> albums = albumRepository.findAllJoinFetch();
for (Album album : albums) {
System.out.println(album.getSongs().size()); // Song에 접근 !
}
}
Fetch Join 에는 2가지 단점이 있다.
JPA 가 제공하는 Pagable 기능 사용 불가(페이징 API)
1:N 관계가 2개인 Entity를 Fetch Join 사용 불가
임시 해결법은 List -> Set으로 자료구조를 변경 하는 것
EntityGraph
public interface PostRepository extends JpaRepository<Post, Long> {
@EntityGraph(attributePaths = "comments")
@Query("select p from Post p")
List<Post> findAllEntityGraph();
}
위 메서드를 통해 목록을 가져오면 Left Outer Join
을 통해 하위 패키지도 하나의 쿼리로 가져온다
반응형
'JPA' 카테고리의 다른 글
[JPA] QueryDSL 기본 문법 알아보기 (0) | 2023.04.07 |
---|---|
[JPA] 영속성 컨텍스트 이해 및 정리 (0) | 2023.04.03 |
[JPA] Auditing, Projections, Pageable 이해하기 (0) | 2023.03.30 |
[Java] JPA 최적화(완벽 요약) (0) | 2023.03.30 |
Spring Boot JPA 핵심 요약(이거만 보면 이해한다) (0) | 2023.03.15 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 다운로드
- java
- 논블로킹
- spring
- 공간쿼리
- github
- Excel
- Spring Security
- Index
- jpa
- database
- jenkins
- 쓰레드
- 스프링
- spring mvc
- spring boot
- 네트워크
- DispatcherServlet
- lock
- db
- 영속성 컨텍스트
- thread
- 비동기
- 인덱스
- R-Tree
- oauth2
- mysql
- 데이터베이스
- GIS
- TCP
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함