OneToOne 양방향 지연로딩

213kky·2024년 9월 22일

프로젝트 진행도중 불필요한 쿼리가 발생하는 것을 보게 되었다.

Hibernate: 
    select
        m1_0.id,
        m1_0.created_at,
        m1_0.email,
        m1_0.is_deleted,
        m1_0.is_enabled,
        m1_0.modified_at,
        m1_0.password,
        m1_0.provider 
    from
        member_auth m1_0 
    where
        m1_0.id=? 
        and (
            m1_0.is_deleted = false
        )
Hibernate: 
    select
        m1_0.id,
        m1_0.avatar_path,
        m1_0.created_at,
        m1_0.email,
        m1_0.introduce,
        m1_0.is_deleted,
        m1_0.link_1,
        m1_0.link_2,
        m1_0.link_3,
        m1_0.modified_at,
        m1_0.nickname 
    from
        member_info m1_0 
    where
        m1_0.id=? 
        and (
            m1_0.is_deleted = false
        )
Hibernate: 
    select
        m1_0.id,
        m1_0.allow_anonymous,
        m1_0.allow_global_question,
        m1_0.created_at,
        m1_0.email_notice,
        m1_0.is_deleted,
        m1_0.is_space_paused,
        m1_0.modified_at 
    from
        member_setting m1_0 
    where
        m1_0.id=? 
        and (
            m1_0.is_deleted = false
        )

상황을 설명하자면

  1. 회원에 대한 정보가 auth, info, setting으로 나누어 설계되어 있다.

  2. 많은 메소드에서 현재 로그인한 회원의 정보를 가져와 활용하는 부분이 있고 기본적으로 auth 부분을 조회하도록 되어있다.

  3. 인증 이외에 기본 정보 또는 세팅 정보가 필요하다면 이후에 .get()을 이용해 LAZY로딩을 하려 했다.

그런데 auth를 조회하는 코드에 info, setting까지 조회하는 쿼리를 보게 되었고 연관관계 설정 옵션인 fetch = FetchType.LAZY가 동작하지 않는 것 같은 현상이 있었다. 어느 부분에 문제가 있는지 차례대로 확인을 해 보았다.

먼저 연관관계 설정 옵션인 fetch = FetchType.LAZY을 각 엔티티에서 확인해 보았지만 모두 정상적으로 설정이 되어있었다.

두번째로 서비스단에서 자식 엔티티를 LAZY로딩 하려는 코드가 있는지 확인 해 보았으나 그런 코드는 존재하지 않았다.

관련 내용을 검색하다 OneToOne 은 양방향 매핑의 경우 지연로딩이 적용되지 않는 문제가 존재한다.
는 글을 발견했다.

단방향 @OneToOne 관계에서는 지연로딩 적용에 문제가 없고,
@OneToOne 양방향 연관 관계에서 연관 관계의 주인이 아닌 쪽 엔티티를 조회할 때, Lazy로 동작할 수 없다고 한다.

이를 해결하려면 아래와 같은 방식들이 있다고한다.

  1. optional=false 설정을 통해 무조건 Lazy Loading 을 시킨다. 이를 통해 반드시 연관 시 값이 존재함이 보장되어야 한다.
    PrimaryKeyJoin 의 경우에는 optional=false 일 경우에 데이터 저장 순서가 꼬여버린다. 정상적인 optional=false가 작동하려면 ForeignKey Join을 해야한다.
  2. fetch join 으로 같이 가져와 해결 가능
  3. 억지로 엔티티에 FK 를 집어 넣어 해결
  4. @ElementCollection 을 통해 컬렉션을 사용하고 unique 조건으로 데이터가 오직 1개만 들어가게 만든다.
  5. OneToOne -> OneToMany + ManyToOne 분리 방법

나는 이중에 2번 방법을 선택했다.

1번방식은 pk를 fk로 가져와 사용하기에 안될거라 생각했고,
3번 방식은 필드를 하나 추가하면서 억지로 fk를 집어넣어 해결하고 싶지 않았고 좋은 방식이라고 생각하지 않았기 때문이다.
5번 방식은 억지 방법으로 OneToOne을 OneToMany, ManyToOne 으로 분리하여 Lazy 조회를 유지하는 방법인데 이것도 좋은 방식은 아닌 것 같다.


참고 사이트
[JPA] @OneToOne 양방향 맵핑 지연 로딩 미적용 문제 개선
OneToOne 관계는 과연 지연로딩이 되는가?

profile
since 2022

0개의 댓글