문제
JPA를 사용하며 테스트를 통해 실제 수행되는 쿼리를 수시로 체크하는데, 일대일(1:1) 양방향 연관관계를 지니는 테이블에서 FetchType.LAZY가 적용되지 않아 불필요한 쿼리가 호출되는 것을 발견하였다.
아래는 문제가 되는 SHELTER와 SHELTER_USER의 연관관계 다이어그램이다.
SHELTER와 SHETLER는 1:1 양방향 연관관계로 매핑되어 있으며, 대상 테이블인 SHELTER_USER에 외래키를 두고 있다. 문제는 SHELTER를 조회 시 SHELTER_USER 테이블도 즉시 로딩되어 아래와 같이 쿼리가 두 번 호출된다.
2023-08-11T17:58:58.461+09:00 DEBUG 2272 --- [nio-8080-exec-3] org.hibernate.SQL :
/* <criteria> */ select
s1_0.shelter_no,
s1_0.created_date,
s1_0.detail_address,
s1_0.email,
s1_0.last_modified_date,
s1_0.shelter_name,
s1_0.street_address,
s1_0.zip_code
from
shelter s1_0
where
s1_0.shelter_name=?
2023-08-11T17:58:58.472+09:00 DEBUG 2272 --- [nio-8080-exec-3] org.hibernate.SQL :
select
s1_0.shelter_user_no,
s1_0.created_date,
s1_0.last_modified_date,
s1_0.shelter_no,
s1_0.user_no
from
shelter_user s1_0
where
s1_0.shelter_no=?
원인
@OneToOne 양방향 연관 관계에서는 연관관계의 주인이 아닌 쪽 엔티티를 조회 시 지연 로딩이 동작하지 않는다. 일대일 양방향 관계는 외래 키가 있는 곳이 연관관계의 주인으로, SHELTER_USER가 연관관계의 주인이며, 결과적으로 연관관계의 주인이 아닌 Shelter 엔티티를 조회할 때 연관된 ShelterUser 또한 즉시 로딩으로 조회 된 것이다.
지연 로딩으로 설정해도 항상 즉시 로딩되는 이 문제는 JPA의 구현체인 Hibernate에서 프록시 기능의 한계로 인해 발생한다.
프록시 객체를 만들기 위해서는 연관 객체에 값이 있는지 없는지 알아야 한다. ShelterUser는 Shelter의 FK를 지니고 있지만, Shelter는 ShelterUser의 값을 확인하기 위해서는 무조건 ShelterUser를 조회해야 한다. 결과적으로 ShelterUser 조회 쿼리가 발생하기 때문에 Hibernate는 프록시 객체를 만들 필요가 없어진다. 따라서 지연 로딩으로 설정해도 동작하지 않는 것이다.
해결 방법
나의 경우 Shelter를 통해 ShelterUser를 조회할 필요가 없기에, 1:1 단방향 연관관계로 수정하여 쿼리가 두 번 나가는 문제를 해결할 수 있었다.
Reference
https://wave1994.tistory.com/156
Spring boot :: JPA에서 OneToOne 관계 N+1 문제 정리
이번에 간단하게 Entity를 조회하는 API인데 성능이 생각보다 안좋은 이슈를 발견하게 되었다. 그래서 Entity를 확인해보니 OneToOne 관계를 사용하고 있었고 로그를 확인 했을 때 쿼리가 한 번이 발생
wave1994.tistory.com
'Coding' 카테고리의 다른 글
[JAVA] boolean 타입의 is prefix와 Lombok, Jackson (0) | 2024.09.11 |
---|---|
[Spring Security] Filter에서 JWT 예외 처리 (1) | 2024.01.28 |
[Spring] @JsonInclude와 @JsonIgnore (0) | 2023.07.12 |
[AWS S3] 여러 객체 삭제 (0) | 2023.07.10 |
[20230628] 오늘의 삽질 (0) | 2023.06.28 |