2023-10-30-TIL
Today I Learned
Optional @OneToOne Mapping in Hibernate
일대일 관계에서 상대가 없을 수도 있는 경우를 어떻게 매핑하는지 정리했다. 예를 들어 회원(Member)과 회원 상세(MemberDetail)가 일대일인데, 상세 정보는 나중에 입력하는 경우다.
Jakarta Persistence의 @OneToOne Javadoc에서 관련 속성은 세 가지다.
optional: 관계가 비어 있어도 되는지 정한다. 기본값은true다.false로 두면 null이 아닌 관계가 항상 있어야 한다.fetch: 지연 로딩할지 즉시 로딩할지 정한다.@OneToOne의 기본값은EAGER다.mappedBy: 관계의 주인이 아닌 반대쪽에만 쓰고, 주인 쪽의 필드 이름을 적는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@OneToOne(mappedBy = "member", fetch = FetchType.LAZY)
private MemberDetail detail; // 없을 수 있다
}
@Entity
public class MemberDetail {
@Id @GeneratedValue
private Long id;
@OneToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", unique = true)
private Member member; // 상세는 반드시 회원에 속한다
}
외래 키는 관계의 주인인 MemberDetail 테이블에 있다. 상세는 회원 없이 존재할 수 없으니 그쪽에 optional = false를 두고, 회원 쪽은 상세가 없을 수 있으니 기본값인 optional = true를 그대로 쓴다. Hibernate 사용자 가이드의 양방향 @OneToOne 예제도 같은 구조로, 부모(Phone)가 mappedBy를 갖고 자식(PhoneDetails)이 외래 키를 갖는다. 가이드에 따르면 Hibernate는 자식 쪽을 가져올 때 유일성을 확인하고, 같은 부모에 자식이 둘 이상 연결돼 있으면 예외를 던진다.
주의할 점은 @OneToOne의 기본 fetch가 EAGER라는 것이다. 명시하지 않으면 회원을 조회할 때마다 상세도 함께 조회된다. 그리고 외래 키가 없는 mappedBy 쪽은 자기 테이블만 봐서는 상대가 있는지 알 수 없다. 그래서 LAZY를 적어도 실제로 몇 번 쿼리가 나가는지는 SQL 로그로 확인하는 것이 확실하다.
링크한 Stack Overflow 질문은 hbm XML 매핑 시절의 같은 고민이다. 사용자와 마지막 방문 페이지가 일대일인데, 새로 만들어서 아직 방문한 페이지가 없는 사용자도 저장할 수 있게 하려면 매핑을 어떻게 바꾸느냐는 질문이다.
- https://stackoverflow.com/questions/2784228/optional-one-to-one-mapping-in-hibernate
- https://www.baeldung.com/jpa-one-to-one
- https://www.logicbig.com/examples/jpa/jpa-one-to-one-optional-example.html
댓글
아직 댓글이 없습니다