문제상황
- User와 Car는 OneToOne 양방향 관계입니다.
- 연관관계의 주인은 User입니다.
성능 향상을 위해, @OneToOne 관계의 FetchType은 거의 항상 LAZY
전략을 사용하고 있었습니다.
(물론, 어플리케이션의 특성에 따라 대부분 항상 같이 조회된다라면 EAGER
를 사용할 수 있겠지만, 그래도 FetchType은 LAZY로 두고 필요한 곳에서 FetchJoin
을 사용하는 편입니다.)
그런데, 분명 LAZY 전략이 적용되어 있지만, 조회 시에 연관관계를 로딩하기 위해 즉시, 추가 쿼리가 발생하는 상황이 발생했습니다.
원인 파악
원인 파악을 위해 여러 시도를하다가, 연관관계의 주인
인 User를 조회하는 경우에는, LAZY Loading이 정상 작동함을 확인할 수 있었습니다.
반대로, 연관관계의 주인이 아닌 Car를 조회하는 경우에는 EAGER
Loading이 발생하는 것을 볼 수 있었습니다.
mappedby
우선 mappedBy를 적용하면 어떤 일이 발생하는지를 알아야 할 것 같습니다.
mappedBy는 양방향 연관관계에서 연관관계의 주인이 아님을 나타낼 때 사용합니다. mappedBy를 사용하면, 주인이 아닌 테이블에서 주인인 테이블을 참조하기 위한(불필요한) 레퍼런스를 생성하지 않도록 할 수 있습니다.
반대로, mappedBy가 적용되지 않는다면, Car에도 User(Owner)를 참조하기 위한 컬럼이 추가되는 것을 볼 수 있습니다.
Proxy에 의한 문제
JPA의 Lazy Loading은 Proxy
에 의해서 구현이 됩니다.
연관관계에 해당하는 reference에 Proxy를 저장해두고, 실제로 연관관계 내부의 필드에 참조가 발생하는 순간 Proxy의 엔티티를 초기화(DB로 부터 조회)합니다.
다시말해, JPA는 엔티티를 초기화하는 시점에 연관관계에 해당하는 reference field에 Proxy를 설정할지, null을 설정할지를 알아야한다
는 말입니다.
그래야 Car의 Owner를 참조할 때 DB로부터 User를 불러와 초기화를 할지, Owner가 없음을 알려줄지(return null)을 알 수 있기 때문입니다.
하지만, mappedBy가 설정되는 경우, Car테이블에는 Owner에 대한 필드가 존재하지 않고, 이 때문에 Owner에 해당하는 연관관계 reference field에 어떤 값을 넣어줄지를 알 수 없습니다. 이 때문에 fetchType이 LAZY이더라도, EAGER 전략처럼 동작하게 됩니다.
참조
https://thorben-janssen.com/hibernate-tip-lazy-loading-one-to-one/
http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html
'FrameWork > Spring JPA' 카테고리의 다른 글
Spring JPA - JPA N+1 문제 완전 정리 (0) | 2021.08.16 |
---|---|
Spring JPA - JPA를 이용해 Commerce App 만들기 - 10 (상품 후기 기능 확장) (0) | 2020.11.10 |
Spring JPA - JPA를 이용해 Commerce App 만들기 - 9.2 (카테고리 기능 기존 시스템과 결합) (1) | 2020.11.09 |
Spring JPA - JPA를 이용해 Commerce App 만들기 - 9.1 (무한카테고리 구현 및 Redis를 이용한 캐싱) (8) | 2020.11.09 |
Spring JPA - JPA를 이용해 Commerce App 만들기 - 8 (무한스크롤, 페이지네이션, 컬렉션 조회 최적화, N+1 문제해결) (0) | 2020.11.08 |