@OneToOne 연관관계에서 Lazy(지연로딩)으로 설정하기?

임원재·2025년 4월 10일
0

SpringBoot

목록 보기
14/18
  • OneToOne매핑으로 MemberRecruit를 1:1 양방향 관계를 맺었다.

  • 이때 mappedBy를 통해 Member엔티티가 Recruit의 id를 외래키로 가진 관계로 표현하였다.

문제상황

  • 위와 같은 상황에서 recruit의 이름 필드로 Recruit를 조회하는 repository 메서드를 호출하였다. (이때 이름 필드는 unique로 설정하였다.)
  • Hibernate쿼리에서 Recruit테이블에서 Select하는 쿼리만 나올 것이라 예상했지만, 아래와 같은 쿼리가 나오게 되었다.
[Hibernate] select r1_0.recruit_id,r1_0.core_skill_id,
r1_0.created_time,r1_0.modified_time,r1_0.name 
from recruit r1_0 
where r1_0.name=?
[Hibernate] select m1_0.member_id,m1_0.member_status,m1_0.birth_date,
m1_0.created_time,m1_0.education,m1_0.email,m1_0.imgurl,
m1_0.inactive_date,m1_0.modified_time,m1_0.name,
m1_0.password,m1_0.recruit_id,m1_0.role,m1_0.school_status,m1_0.type 
from member m1_0
where m1_0.recruit_id=?
  • recruit테이블에 대한 쿼리 이후, member테이블의 외래키에 대한 추가 쿼리가 발생하였다.
  • 그동안 진행한 JPA쿼리와는 사뭇 다른 양상을 보여 당황했었다.
  • 연관관계의 주인이 아닌 Recruit에서 연관관계의 주인인 Member에 대한 추가 쿼리가 나갈리가 없다고 생각했다.
  • 혹여나 member엔티티의 필드를 get해오는 코드가 작성되었는지 확인했지만, 그런 코드는 없었다.
  • 이에 hibernate 문서에서 lazy(지연로딩), 연관관계에 대한 내용을 찾아보았다. 아래와 같은 내용을 찾을 수 있었다.

  • 결론은 @ManyToOne과 @OneToOne 연관관계는 프록시를 사용하지 않기에 페치전략을 지연로딩이 아닌 즉시로딩이 기본값이라는 것이었다.
  • 근데 @ManyToOne관계에서 LAZY전략을 많이 써오지 않았던가...?
  • 진짜 EAGER인지 테스트하기 위해 @ManyToOne관계에 있는 엔티티에 기본 전략이 적용되도록 아무것도 설정하지 않고 해당 엔티티를 조회하는 테스트 쿼리를 만들어보았다.

  • 결과는 could not initialize proxy - no Session예외가 발생했다.
  • 이는 문서와는 다르게 @ManyToOne관계에서 기본 전략이 LAZY로 동작하고 있음을 보여준다.
  • 해당 이슈는 좀 더 찾아본 결과, JPA명세 상에서의 기본 전략은 EAGER가 맞지만, JPA를 구현한 구현체 Hibernate에서는 이를 @ManyToOne관계를 LAZY로 설정하여 적용한다는 것을 알게 되었다.
  • 즉, @OneToOne만 즉시로딩이라고 생각하면 될 것 같다.

일대일 관계

  • 위 이슈로 @ManyToOne과 @OneToOne관계에 대해 다시 한 번 생각하게 되었다.
  • 정리하자면 두 연관관계 모두 기본전략이 즉시로딩이지만, @ManyToOne연관관계는 현재 지연로딩으로 동작한다.
  • 다시 말하면 @OneToOne관계는 즉시로딩이 기본값이어야 하는 이유가 있는 것이다.
  • 나는 그 이유가 연관관계의 주인에 있다고 생각한다.
  • 위에서 MemberRecruit같은 일대일 연관관계에서 연관관계의 주인을 Member로 설정하였다. Recruit에서 Member를 조회하는 일 보다 Member에서 Recruit를 조회하는 일이 더 많았기 때문이다.
  • 해당 상황에서 MemberRecruit엔티티는 1:1관계이므로, 실제 외래 키는 연관관계의 주인인 Member가 지니게 된다. 즉, Recruit에서는 관계를 맺고 있는 Member를 확인할 수 없다.
  • 다시 말해 관계를 맺고 있는 엔티티를 찾지 못한다면 프록시 기술을 적용할 수 없고, 이는 지연로딩을 사용할 수가 없다는 것이다.
  • 결론은, 일대일 연관관계에서는 연관관계의 주인이냐 아니냐에 따라 지연 로딩이 가능한지 여부가 달라진다. 이에 1:1관계에서 기본전략을 EAGER로 설정했다고 생각한다.

문제 해결

  • 다시 MemberRecruit로 돌아와보자
  • 문제 상황은 Recruit를 조회하는데 Member쿼리도 함께 나간다는 것이었다.
  • 요구사항을 분석하였을 때, RecruitMember를 조회하는 일은 일어나지 않을 것이라고 판단했다.
  • Recruit에서 설정한 Member를 향한 단방향 연관관계를 없앰으로써, MemberRecruit를 1:1 단방향 연관관계로 설정하게 되었다.
  • 이 방법으로 Recruit를 조회할 때 Member에 대한 연관관계가 없기 때문에 Member에 대한 쿼리는 발생하지 않을 것이다. 하지만 Member를 조회할 때 즉시로딩으로 인해 Recruit엔티티는 언제나 조회될 것이다.

  • 이와 같이 Recruit조회 시 Member에 대한 쿼리가 발생하지 않음을 확인할 수 있다.
  • 1:1연관관계의 기본전략이 LAZY임을 알았으니, 마지막으로 Member엔티티 자체를 조회할 때 Recruit엔티티도 함께 조회되는지 테스트하였다.

  • 위와 같이 설정하고 조회를 진행했더니

  • 다음과 같이 recruit엔티티도 함께 조회되어 즉시로딩임을 확인할 수 있다.

0개의 댓글