먼저 문제 상황은 이러하다.
상황 설명 :
1. bbs와 user 는 ManyToOne 양방향 관계이고 Lazy로딩으로 설정되어있다.
2. 게시글을 페이징하여 가져올 필요가 있다.
게시글을 가져올때 작성자 닉네임을 함께 가져와야 하기 때문에 user 테이블과 fetch join(EntityGraph)를 통해 성능최적화를 하였다.
3. 위 사항까지만 있었을때는 쿼리 한번으로 모든 정보를 가져올 수 있었고, 성능문제가 발생하지 않았다.
4. 새로운 기능 추가를 위해 game 테이블을 추가로 생성했다. user 와 game은 OneToOne양방향 관계이며 Lazy로딩으로 설정되어있다.
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 발동조건
- nullable이 허용되지 않는 1:1 관계. 즉, 참조 객체가 optional = false 로 지정할 수 있는 관계여야 한다.
- 양방향이 아닌 단방향 1:1 관계여야한다.
- @PrimaryKeyJoin은 허용되지 않는다. 부모와 자식 엔티티간의 조인컬럼이 모두 PK의 경우를 의미한다.
자세한 이유는 위 블로그를 참고하자.
이 상황에서는 2번 조건이 해당되지 않는다. 그렇기 때문에 lazy로딩으로 설정을 했으나, earger로딩으로 작동을 한 것이다.
해결 방법 :
2가지 방법을 적용해보았고, 둘 다 정상적으로 N+1 문제가 해결 되었다.
1. OneToOne 단방향 연관관계로 수정하는 방법
user 도메인에서 game부분을 주석처리 하여, 양방향 -> 단방향으로 바꿔준다.
2. user와 game을 OneToMany 양방향 연관관계로 수정하는 방법
나의 경우에 1번 해결법이 더 적합한 것 같아 1번으로 적용하고 마무리 하였다.
'컴퓨터 > Spring + JPA' 카테고리의 다른 글
[인텔리제이 DTO generator] json data 자동으로 entity class 생성하기 (0) | 2021.06.29 |
---|---|
[해결방법] JPA MySQL server version for the right syntax to use (0) | 2021.03.13 |
관계형 db vs 그래프 db에 대한 고민 (0) | 2021.03.07 |
[스프링] spring bean 과 java bean의 차이점! (0) | 2020.11.10 |
[해결방법] [spring boot] ResourceHttpRequestHandler - Path with "WEB-INF" or "META-INF" (0) | 2020.11.07 |