본문 바로가기

컴퓨터/Spring + JPA

[Spring JPA] OneToOne N+1 문제 (원인 해결방법 포함)

반응형

먼저 문제 상황은 이러하다.

 

상황 설명 :

 

1. bbs와 user 는 ManyToOne 양방향 관계이고 Lazy로딩으로 설정되어있다. 

user도메인의 일부
bbs도메인의 일부

2. 게시글을 페이징하여 가져올 필요가 있다.

게시글을 가져올때 작성자 닉네임을 함께 가져와야 하기 때문에 user 테이블과 fetch join(EntityGraph)를 통해 성능최적화를 하였다.

spring data jpa


3. 위 사항까지만 있었을때는 쿼리 한번으로 모든 정보를 가져올 수 있었고, 성능문제가 발생하지 않았다.

 

 

4. 새로운 기능 추가를 위해 game 테이블을 추가로 생성했다. user 와 game은 OneToOne양방향 관계이며 Lazy로딩으로 설정되어있다.

 

game 도메인의 일부
user도메인의 일부

 

5. game테이블을 생성했는데, 예상하지 못한 곳에서 문제가 발생했다. 게시판을 조회하니 아래와 같은 의도하지 않은 쿼리가 게시글 수 만큼 추가로 발생했다. ( N+1 문제 발생 )

 

문제 원인을 찾아가는 생각의 과정 :

1. N+1 문제가 발생했다는 것은 Lazy 로딩이 작동하지 않은 것 같다. -> eager 로딩으로 작동

2. bbs와 game 사이에는 연관관계가 없으니, bbs -> user -> game 으로 번져가는.. 즉 user와 game 사이에서 eager 로딩이 발생했을 것이다.

3. 분명히 Lazy 로딩으로 지정했는데 왜 ?? eager 로딩이 발생했는지 알아보면 될듯!

 

문제 원인 : 

예상한대로 OneToOne 관계에서 Lazy 로딩이 작동하려면 특정 조건이 있다고 한다. 

 

JPA에서의 OneToOne 관계에서의 Lazy Loading 발동조건

  1. nullable이 허용되지 않는 1:1 관계. 즉, 참조 객체가 optional = false 로 지정할 수 있는 관계여야 한다.
  2. 양방향이 아닌 단방향 1:1 관계여야한다.
  3. @PrimaryKeyJoin은 허용되지 않는다. 부모와 자식 엔티티간의 조인컬럼이 모두 PK의 경우를 의미한다.

참고한 블로그 링크

자세한 이유는 위 블로그를 참고하자. 

 

이 상황에서는 2번 조건이 해당되지 않는다. 그렇기 때문에 lazy로딩으로 설정을 했으나, earger로딩으로 작동을 한 것이다. 

 

해결 방법 : 

2가지 방법을 적용해보았고, 둘 다 정상적으로 N+1 문제가 해결 되었다.

 

1. OneToOne 단방향 연관관계로 수정하는 방법 

user 도메인에서 game부분을 주석처리 하여, 양방향 -> 단방향으로 바꿔준다.

2. user와 game을 OneToMany 양방향 연관관계로 수정하는 방법

user 도메인의 일부

 

game 도메인의 일부

나의 경우에 1번 해결법이 더 적합한 것 같아 1번으로 적용하고 마무리 하였다.

반응형