일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 4-way-handshake
- zipWith
- try-catch-finally
- jetbrain
- n+1
- java
- springboot
- GC
- circuitbreaker
- try-catch
- 동작방식
- 날짜쿼리
- execution engine
- closeable
- tcp
- jvm
- Kotlin
- feign
- optional
- resilience4j
- try-with-resources
- webflux
- 람다표현식
- wagon-ssh
- Hotspot VM
- Class Loader
- AutoCloseable
- Runtime data area
- 코딩테스트
- intelij
- Today
- Total
JuBin's personal study blog
[JPA] JPA N+1 문제와 해결방법 본문
JPA N+1 쿼리 문제란?
JpaRepository에 정의한 인터페이스 메소드를 실행하면 JPA는 메소드 이름을 분석해 JPQL을 생성하여 실행하게 됩니다. 이 과정에서 N+1 쿼리 문제가 발생하게 됩니다.
ex)
먼저 N+1 문제에 대해 먼저 간단히 설명하자면 1:N 관계를 갖는 Entity 클래스를 작성하고(Album Entity, Song Entity)
JPQL로 객체를 조회할때
1) EAGER 전략으로 데이터를 조회 할때
2) LAZY 전략으로 데이터를 조회 후 하위 엔티티(Song Entity)를 조회 할때
N+1 이슈가 발생하게 됩니다.
예시를 통하여 확인해 봅시다.
※ JPQL이란?
JPQL은 SQL을 추상화한 객체지향 쿼리 언어로 특정 DB에 종속되지 않고 Entity와 필드 이름을 가지고 쿼리를 만듭니다.
Album Entity
@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<>();
}
Song Entity
@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 Case 1 (Album Entity의 songs 필드 패치전략이 EAGER(즉시로딩 일때)
@Test
public void EAGER_N1_TEST throws Exception {
List<Album> albums = albumRepository.findAll(); // N+1문제 발생
}
JPA N+1 발생
하나의 Album 조회시 10개의 Song row 조회하는 문제 발생

Test Case 2 (LAZY, Album Entity 조회 후 하위 Song Entity 조회 시 발생
@Test
public void LAZY_N1_TEST throws Exception {
List<Album> albums = albumRepository.findAll(); // 지연로딩으로 인해 N+1문제가 발생하지 않음
List<Song> songList = null;
for (Album album : albums) {
songList = album.getSongList();
log.info("song size = {}", songList.size()); // N+1문제 발생
{
}
JPA N+1 발생
하나의 Album 조회시 10개의 Song row 조회하는 문제 발생

N+1 해결 방안
1. JPQL 패치 조인 사용
- JPQL fetch join 키워드를 사용해 Album 조회 시 songList도 같이 join해서 조회합니다.
- LAZY, EAGER 두개의 전략에 해당되는 해결방법 입니다.
@Repository
public interface AlbumRepository extends JpaRepository<Album, Long> {
@Query("select p from Album p left join fetch p.songList")
List<Album> findAllWithFetchJoin();
}
하지만 패치조인은 JPA가 제공하는 Pageable 기능을 사용할 수 없고(페이징 처리 API)
1:N 관계가 2개인 Entity에서는 사용 할 수 없습니다.
또한 데이터가 많아지면 OOM(Out Of Memory)가 발생할 수도 있습니다.
'JPA' 카테고리의 다른 글
[Spring-Data-JPA] 메소드 작명 키워드 (0) | 2021.02.15 |
---|