OneToOne
매핑으로 Member
와 Recruit
를 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관계는 즉시로딩이 기본값이어야 하는 이유가 있는 것이다.
- 나는 그 이유가 연관관계의 주인에 있다고 생각한다.
- 위에서
Member
와 Recruit
같은 일대일 연관관계에서 연관관계의 주인을 Member
로 설정하였다. Recruit
에서 Member
를 조회하는 일 보다 Member
에서 Recruit
를 조회하는 일이 더 많았기 때문이다.
- 해당 상황에서
Member
와 Recruit
엔티티는 1:1관계이므로, 실제 외래 키는 연관관계의 주인인 Member
가 지니게 된다. 즉, Recruit
에서는 관계를 맺고 있는 Member
를 확인할 수 없다.
- 다시 말해 관계를 맺고 있는 엔티티를 찾지 못한다면 프록시 기술을 적용할 수 없고, 이는 지연로딩을 사용할 수가 없다는 것이다.
- 결론은, 일대일 연관관계에서는 연관관계의 주인이냐 아니냐에 따라 지연 로딩이 가능한지 여부가 달라진다. 이에 1:1관계에서 기본전략을 EAGER로 설정했다고 생각한다.
문제 해결
- 다시
Member
와 Recruit
로 돌아와보자
- 문제 상황은
Recruit
를 조회하는데 Member
쿼리도 함께 나간다는 것이었다.
- 요구사항을 분석하였을 때,
Recruit
로 Member
를 조회하는 일은 일어나지 않을 것이라고 판단했다.
Recruit
에서 설정한 Member
를 향한 단방향 연관관계를 없앰으로써, Member
와 Recruit
를 1:1 단방향 연관관계로 설정하게 되었다.
- 이 방법으로
Recruit
를 조회할 때 Member
에 대한 연관관계가 없기 때문에 Member
에 대한 쿼리는 발생하지 않을 것이다. 하지만 Member
를 조회할 때 즉시로딩으로 인해 Recruit
엔티티는 언제나 조회될 것이다.

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


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