들어가며
6강은 연관관계를 맺을때 일대일 / 다대일 / 일대다 / 다대다 모든 케이스의 방향에 대한 설명이다.
- 연관관계 매핑 시 다중성 / 단방향, 양방향 / 연관관계 주인 3가지를 고려해야 함
- 다중성
- 다대일(@ManyTonOne) / 일대다 / 일대일 / 다대다
- 다중성을 판단하기 어려울 때는 반대방향을 생각해보면 된다.
- 일대다의 반대방향을 항상 다대일이고, 일대일이나 다대다의 반대방향은 동일
- 단반향, 양방향
- 테이블은 외래 키 하나로 조인을 사용해서 양방향으로 쿼리가 가능하므로 사실상 방향이라는 개념 없음
- 객체는 필드를 가지고 있는 객체만 연관된 객체를 조회할 수 있고, 한쪽만 참조하는 것을 단방향 / 양쪽이 서로 참조하는 것을 양방향 관계라 한다.
- 연관관계의 주인
- 엔티티를 양방향으로 매핑하면 2곳에서 서로를 참조
- 따라서 객체의 연관관계를 관리하는 포인트는 2곳
- Jpa는 두 객체 연관관계 중 하나를 정해서 디비 외래 키를 관리하는데 이것을 연관관계의 주인이라고 함
- 외래키를 가진 테이블과 매핑한 엔티티가 외래키를 관리하는 게 효율적이므로 보통 이곳을 연관관계의 주인으로 선택
- 6.1 다대일
- 디비 테이블의 일, 다 관계에서 외래 키는 항상 다쪽에 있음
- 6.1.1 다대일 단방향 [N:1]
- 회원은 팀 엔티티를 참조할 수 있지만 반대로 팀에는 회원을 참조하는 필드가 없으므로 회원과 팀은 다대일 단방향 연관관계이다.
- 6.1.2 다대일 양방향 [N:1, 1:N]
- 양방향은 외래 키가 있는 쪽이 연관관계의 주인이다
- 일대다 다대일 연관관계는 항상 다에 외래키가 있다.
- 회원과 팀에서는 다쪽인 MEMBER 테이블이 외래 키를 가지고 있으므로 Member.team이 연관관계의 주인
- Jpa는 외래 키를 관리할 때 연관관계의 주인만 사용
- 주인이 아닌 Team.members는 조회를 위한 JPQL이나 객체 그래프를 탐색할 때 사용
- 양방향은 외래 키가 있는 쪽이 연관관계의 주인이다
- 6.2 일대다
- 일대다 관계는 다대일 관계의 반대 방향
- 일대다 관계는 엔티티를 하나 이상 참조할 수 있으므로 자바 컬렉션인 Collection, List, Set, Map 중에 하나를 사용해야 함
- 6.2.1 일대다 단방향 [1:N]
- 하나의 팀은 여러 회원을 참조할 수 있는데 이런 관계를 일대다 관계라 함
- 그리고 팀은 회원들을 참조하지만 반대로 회원은 팀을 참조하지 않으면 둘의 관계는 단방향이다.
- 일대다 단방향 관계를 매핑할 때는 @JoinColumn을 명시해야 한다. → 확인!
- 그렇지 않으면 JPA는 연결 테이블을 중간에 두고 연관관계를 관리하는 조인 테이블 전략을 기본으로 사용해서 매핑한다.
- 일대다 단방향 매핑의 단점
- 매핑한 객체가 관리하는 외래 키가 다른 테이블에 있다는 점
- 본인 테이블에 외래 키가 있으면 엔티티의 저장과 연관관계 처리를 INSERT SQL 한 번으로 끝낼 수 있지만 다른 테이블에 외래 키가 있으면 연관관계 처리를 위한 UPDATE SQL을 추가로 실행해야 한다.
- 일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자
- 일대다 단방향 매핑을 사용하면 엔티티를 매핑한 테이블이 아닌 다른 테이블의 외래키를 관리해야 한다.
- 성능 문제도 있지만 관리도 부담스럽다.
- 문제를 해결하는 방법은 일대다 단뱡향 매핑 대신 다대일 양방향 매핑을 사용하는 것
- 상황에 따라 다르겠지만 일대다 단방향 매핑보다는 다대일 양방향 매핑을 권장
- 6.2.2 일대다 양방향 [1:N, N:1]
- 일대다 양방향 매핑은 존재하지 않고 다대일 양방향 매핑을 사용해야 한다.
- 일대다나 다대일이나 사실 똑같은 말이며 왼쪽을 연관관계의 주인으로 가정해서 분류하는 것 -> 예를 들어 다대일이면 다가 연관관계 주인
- 양방향 매핑에서 @OneToMany는 연관관계 주인이 될 수 없다. 관계형 디비 특성상 일대다, 다대일 관계는 항상 다 쪽에 외래 키가 있기 때문
- 그렇다고 일대다 양방향 매핑이 완전히 불가능 것은 아님
- 일대다 단방향 매핑 반대편에 같은 외래 키를 사용하는 다대일 단방향 매핑을 읽기 전용으로 하나 추가하면 된다
- 대신 이때 일대다 단방향 매핑과 같은 외래 키 컬럼을 매핑하게 되므로 둘 다 같은 키를 관리하게 되어 문제가 발행할 수 있어 반대편인 다대일 쪽은 insertable = false, updatable = false로 설정해서 일기만 가능하게 해줘야한다.
- 이 방법은 굳이 사용 권장하지 않고 될 수 있으면 다대일 양방향 매핑을 사용 권장
- 6.3 일대일 [1:1]
- 일대일 관계는 양쪽이 서로 하나의 관계만 가짐
- 특징
- 일대일 관계는 그 반대도 일대일 관계다
- 테이블 관계에서 일대다, 다대일은 항상 다쪽이 외래키를 가진다. 반면 일대일 관계는 주 테이블이나 대상 테이블 둘 중 어느 곳이나 외래 키를 가질 수 있다.
- 일대일 관계는 주 테이블이나 대상 테이블 중에 누가 외래 키를 가질지 선택해야 한다.
- 주 테이블에 외래 키
- 주 객체가 대상 객체를 참조하는 것처럼 주 테이블에 외래 키를 두고 대상 테이블 참조
- 외래 키를 개체 참조와 비슷하게 사용할 수 있어서 객체지향 개발자들이 선호
- 주 테이블이 외래 키를 가지고 있으므로 주 테이블만 확인해도 대상 테이블과 연관관계가 있는지 알 수 있는 장점을 가짐
- 대상 테이블에 외래 키
- 전통적인 데이터 베이스 개발자들은 보통 대상 테이블에 외래 키를 두는 것을 선호
- 테이블 관계를 일대일에서 일대다로 변경할 때 테이블 구조를 그대로 유지할 수 있는 장점을 가짐
- 6.3.1 주 테이블에 외래 키
- JPA도 주 테이블에 외래 키가 있으면 좀 더 편리하게 매핑 가능
- 단방향 (주 테이블에 외래 키가 있는 경우)
- 일대일 관계이므로 객체 매핑에 @OneToOne 사용
- 참고로 이 관계는 다대일 단방향과 거의 비슷
- 양방향
- 양방향이므로 연관관계의 주인을 정해야 한다.
- Member가 Locker_Id를 가지고 있으므로 연관관계의 주인이 된다.
- 따라서 Member 엔티티에 있는 Member.locker 가 연관관계 주인이고 반대 매핑인 사물함의 Locker.member는 mappedBy를 선언해서 연관관계의 주인이 아니라고 설정 필요
- 6.3.2 대상 테이블에 외래 키
- 단방향 (대상 테이블에 외래 키가 있는 일대일 관계)
- 일대일 관계 중 대상 테이블에 외래 키가 있는 단방향 관계는 JPA에서 지원하지 않는다.
- 이런 모양으로 매핑할 수 있는 방법도 없다.
- 이 때는 단방향 관계를 Locker에서 Member 방향으로 수정하거나 양방향 관계로 만들고 Locker를 연관관계의 주인으로 설정해야 한다.
- 참고로 JPA2.0부터는 일대다 단방향 관계에서 대상 테이블에 외래 키가 있는 매핑을 허용 (일대일 단방향은 이런 매핑 허용 x)
- 양방향
- 일대일 매핑에서 대상 테이블에 외래 키를 두고 싶으면 이렇게 양방향으로 매핑
- 주 엔티티인 Member 엔티티 대신에 대상 엔티티인 Locker를 연관관계의 주인으로 만들어서 LOCKER 테이블의 외래 키를 관리하도록 함
- 주의점
- 프록시를 사용할 때 외래 키를 직접 관리하지 않는 일대일 관계는 지연 로딩으로 설정해도 즉시 로딩된다.
- 이러한 현상은 프록시의 한계 때문에 발생하는 문제인데 프록시 대신에 bytecode instrumentation을 사용하면 해결 가능하다. ??
- 단방향 (대상 테이블에 외래 키가 있는 일대일 관계)
- 6.4 다대다 [N:N]
- 관계형 디비는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.
- 그래서 보통 다대다 관계를 일대다, 다대일 관계로 풀어내는 연결 테이블을 사용한다
- 테이블로는 중간에 연결 테이블을 통해서 다대다 관계를 일대다, 다대일 관계로 풀어낼 수 있다.
- 그런데 객체는 테이블과 다르게 객체 2개로 다대다 관계를 만들 수 있다.
- 예로 회원은 컬렉션을 사용해서 상품들을 참조하고 반대로 상품들도 컬렉션을 이용해서 회원들을 참조하면 된다.
- @ManyToMany를 사용하여 다대다 관계를 편리하게 매핑할 수 있다.
- 6.4.1 다대다: 단방향
- 회원 엔티티에 @ManyToMany를 사용하고 @JoinTable(name = “MEMBER_PRODUCT”)를 사용해서 연결 테이블을 바로 매핑
- 따라서 회원과 상품을 연결하는 회원_상품 엔티티 없이 매핑 완료 가능
- @JoinTable 속성
- name : 연결 테이블을 지정
- joinColumns : 현재 방향인 회원과 매핑할 조인 컬럼 정보를 지정
- MEMBER_PRODUCT 테이블은 다대다 관계를 일대다, 다대일 관계로 풀어내기 위해 필요한 연결 테이블일 뿐
- @ManyToMany로 매핑한 덕분에 다대다 관계를 사용할 때는 이 연결 테이블을 신경 쓰지 않아도 됨
- 6.4.2 다대다: 양방향
- 다대다 매핑이므로 역방향도 @ManyToMany를 사용 (양쪽 다)
- 그리고 양쪽 중 원하는 곳에 mappedBy로 연관관계의 주인을 지정 (없는 곳이 주인)
- 6.4.3 다대다: 매핑의 한계와 극복, 연결 엔티티 사용
- @ManyToMany를 사용하면 연결 테이블을 자동으로 처리해주므로 도메인 모델이 단순해지고 여러가지로 편리
- 하지만 이 매핑을 실무에서 사용하기에는 한계가 있음
- 예로 회원이 상품을 주문하면 연결 테이블에 단순히 주문한 회원 아이디와 상품 아이디만 담고 끝나지 않음
- 보통은 연결 테이블에 주문 수량 컬림이나 주문한 날짜 같은 다른 컬럼이 더 필요
- 이렇게 컬럼을 추가하면 더는 @ManyToMany를 사용할 수 없다
- 이유는 주문 엔티티나 상품 엔티티에는 추가한 컬럼들을 매핑할 수 없기 때문
- 결국에는 연결 테이블을 매핑하는 연결 엔티티를 만들고 이곳에 추가한 컬름들을 매핑해야 함.
- 그리고 엔티티 간의 관계도 테이블 관계처럼 다대다에서 일대다, 다대일 관계로 풀어야 함.
- 복합 기본 키
- 회원상품 엔티티는 기본 키가 MEMBER_ID, PRODUCT_ID로 이루어진 복합 기본 키.
- JPA에서 복합 키를 사용하려면 별도의 식별자 클래스를 만들어야 함
- 그리고 엔티티에 @IdClass를 사용해서 식별자 클래스를 지정
- 복합 키를 위한 식별자 클래스의 특징
- 복합 키는 별도의 식별자 클래스로 만들어야 한다.
- Serializable을 구현해야 함
- equals와 hashCode 메서드를 구현해야함
- 기본 생성자가 있어야 함
- 식별자 클래스는 public이어야 함
- @IdClass를 사용하는 방법 외에 @EmbeddedId를 사용하는 방법도 있음
- 식별 관계
- 회원 상품은 회원과 상품의 기본 키를 받아서 자신의 기본 키로 사용
- 이렇게 부모 테이블의 기본 키를 받아서 자신의 기본 키 + 외래 키로 사용하는 것을 디비 용어로 ‘식별 관계’ 라 함
- 복합키를 사용하면 항상 식별자 클래스로 엔티티를 조회 한다. (em.find(MemberProduct.class, memberProductId))
- 6.4.4 다대다: 새로운 기본 키 사용 (복합 키를 사용하지 않고 간단히 다대다 관계를 구성하는 방법)
- 추천하는 기본 키 생성 전략은 디비에서 자동으로 생성해주는 대리 키를 Long값으로 사용하는 것
- 장점은 간편하고 거의 영구히 쓸 수 있으며 비즈니스에 의존하지 않는다.
- 그리고 ORM 매핑 시에 복합 키를 만들지 않아도 되므로 간단히 매핑을 완성할 수 있음
- 이제는 MemberProduct에서 복합 키가 아닌 Order의 개념으로 보아 새로운 기본 키를 사용하고 Member_id와 Product_id 컬럼은 외래 키로만 사용
- 6.4.5 다대다 연관관계 정리
- 다대다 관계를 일대다 다대일 관계로 풀어내기 위해 연결 테이블을 만들 때 식별자를 어떻게 구성할지 선택해야함
- 식별관계 : 받아본 식별자를 기본 키 + 외래 키로 사용
- 비 식별관계 : 받아온 식별자는 외래 키로만 사용하고 새로운 식별자를 추가
- 비식별 관계로 사용하는 것이 복합 키를 위한 식별자 클래스를 만들지 않아도 되므로 단순하고 편리하게 ORM 매핑이 가능하여 해당 방법을 권장
'Book' 카테고리의 다른 글
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (7-2) 고급매핑 (0) | 2022.08.24 |
---|---|
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (7-1) 고급매핑 (0) | 2022.08.17 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (5-2) 연관관계 매핑 기초 (0) | 2022.08.07 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (5-1) 연관관계 매핑 기초 (0) | 2022.08.01 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (4-2) 엔티티 매핑 (0) | 2022.07.27 |
댓글