1) JPA 연관관계 매핑 시 고려사항 3가지
✔ 다중성
✔ 단방향, 양방향
✔ 연관관계의 주인
▼ 다중성 - 애노테이션 정리
- 다대일 : @ManyToOne
- 일대다 : @OneToMany
- 일대일 : @OneToOne
- 다대다 : @ManyToMany
주로 다대일, 일대다, 일대일 관계로 표현하며, 다대다 관계를 가지는 경우 1:N - M:1 관계인 일대다 - 다대일 관계로 풀어서 표현합니다.
▼ 단방향, 양방향
[테이블] : 데이터베이스
- 외래 키 하나로 양쪽 조인이 가능합니다.
[객체] : 자바
- 참조용 필드가 있는 쪽에서만 참조가 가능합니다.
(한쪽만 참조하면 단방향, 양 쪽이 서로 참조하면 양방향)
▼ 연관관계의 주인
- 테이블은 외래 키 하나로 두 테이블이 연관관계를 맺습니다.
- 객체 양방향 관계는 A -> B / B -> A 처럼 참조가 2군데에 있습니다.
객체 양방향 관계에서는 참조가 2군데 있으므로, 둘 중 테이블의 외래 키를 관리할 곳을 지정해야 합니다. ( 연관관계 주인 설정)
* 연관관계의 주인 : 외래 키를 관리하는 참조합니다.
* 주인의 반대편 : 외래 키에 영향을 주지 않고, 단순 조회만 가능합니다.
2) 다대일 [N:1]
Member (회원)은 하나의 Team(팀)에 속할 수 있다. / Team에는 여러 명의 Member(회원)이 존재한다.
* 다대일 단방향
Member -> Team 으로만 조회가 가능하며, Team에서 Member를 조회할 수 없습니다.
그러므로 Team을 참조하고 있는 Member에서 외래 키를 관리합니다.
* 다대일 양방향
단방향이 아닌 양방향 관계는 Member < -> Team로 양쪽이 서로 참조하고 있는 관계입니다.
양방향에서는 외래 키가 있는 쪽이 연관관계의 주인이 됩니다.
(데이터베이스에서는 1:N 관계에서 N 측에서 외래 키를 관리합니다. JPA 다대일 단방향이나 양방향 관계에서도 다인 측에서 외래 키를 관리하는 것이 좋습니다.)
그러므로 객체 Member의 속성 Team team을 테이블 MEMBER의 TEAM_ID(FK)와 연관관계 매핑을 합니다.
3) 일대일 [1:1]
* 일대일 관계는 그 반대도 일대일이다.
* 주 테이블이나 대상 테이블 중에서 외래 키를 선택합니다.
* 외래 키에 데이터베이스 유니크(UNI) 제약조건을 추가하여 중복성을 제거합니다.
✔ 일대일 : 주 테이블에 외래 키 단방향 관계
Member는 하나의 Locker를 이용한다. / Locker는 한 명의 Member가 사용한다.
✔ 일대일 : 주 테이블에 외래 키 양방향 관계
- 다대일 양방향 매핑처럼 외래 키가 있는 곳이 연관관계의 주인입니다.
- 반대편은 mappedBy를 적용하여 조회할 수 있도록 해줍니다.
✔ 일대일 : 대상 테이블에 외래 키 양방향 관계
* 일대일 주 테이블에 외래 키 양방향과 매핑 방법이 같습니다.
일대일 정리
✔ 주 테이블에 외래 키
- 주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래 키를 두고 대상 테이블을 찾음
장점 : 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인이 가능합니다.
단점 : 값이 없으면 외래 키에 null이 허용됩니다.
✔ 대상 테이블에 외래 키
- 대상 테이블에 외래키가 존재하며, 데이터베이스 개발자가 선호합니다.
장점 : 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조를 유지할 수 있습니다.
단점 : 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됩니다.
4) 다대다 [N:M]
* 관계형 데이터베이스는 정규화된 테이블 2개를 다대다 관계로 표현할 수 없습니다.
중간에 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어야 합니다.
Member는 여러 개의 Product(상품)을 주문할 수 있고, Product(상품)은 여러 명의 Member가 구매할 수 있습니다.
위와 같은 다대다 관계를 일대다 + 다대일 관계로 풀어서 표현해야 합니다.
중간에 연결 테이블용 엔티티를 추가합니다.
Member는 여러 개의 Order를 할 수 있으며 하나의 Order는 한 Member가 주문한 것입니다.
Order는 하나의 Product(상품)을 포함하며, 하나의 Product는 여러 개의 Order(주문)에 포함될 수 있습니다.
5) 지연 로딩
@ManyToOne이나 @OneToOne로 매핑된 관계는 한 테이블을 조회하면 자동으로 즉시 로딩 됩니다.
조회하지 않으려고 했던 다른 테이블의 값까지 조회하게 되므로 성능이 저하되는 문제가 발생합니다.
즉시로딩( EAGER )은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵습니다.
특히 JPQL을 실행할 때 N+1 문제가 자주 발생하게 됩니다.
그러므로 모든 연관관계는 지연로딩( LAZY )으로 설정해야 합니다.
연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용합니다.
@XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 해 줍니다.
@ManyToOne(fetch = FetchType.LAZY)
@OneToOne(fetch = FetchType.LAZY)
@XToMany 인 경우는 컬렉션을 조회하기에 JPA에서 기본적으로 지연 로딩으로 설정되어 있습니다.