-
[Spring Data JPA] - 연관 관계 매핑Java/Java Spring 2023. 5. 17. 13:49
Entity 설계에 대해서 공부하다 보니, 연관 관계 매핑이란 말을 심심치 않게 볼 수 있었다. 일대다, 다대일, 단방향, 양방향 등등
그렇다면 모두 다대다 양방향 매핑을 하면 안되는 걸까? 라는 생각으로 공부했던 내용을 정리한다.
연관 관계 매핑이란?
연관관계 매핑 : 객체의 참조와 테이블의 외래 키를 매핑하는 것
연관 관계 매핑 시 고려 요소
1. 방향(Direction)
단방향 / 양방향
객체는 참조용 필드를 가지고 있는 객체만, 연관된 객체를 조회할 수 있다.
두 객체가 서로 참조하는 관계 => 양방향 관계
한 객체에서 다른 객체만 참조하는 관계 => 단방향 객체
테이블의 경우, 외래 키 하나로 양쪽으로 조인이 가능 => 양방향 혹은 방향이 없다 라고 판단 가능
2. 다중성(Multiplicity)
일대다(OneToMany) / 다대일(ManyToOne) / 일대일(OneToOne) / 다대다(ManyToMany)
3. 연관관계의 주인(Owner)
외래 키를 이용해 관계를 맺는데, 양방향 관계 시, 객체 서로가 외래 키를 가질 수 있다. 따라서 두 객체 중 하나가 외래 키를 관리해야 하는데 이 때 관리하는 객체가 연곤관계의 주인이다.
연관관계 주인만이 데이터를 등록, 변경할 수 있고, 주인이 아닌 객체는 읽기만 가능하다.
일대일 단방향 연관관계
장바구니 - 회원 ERD ## Cart Entity @Entity public class Cart { @Id @Column(name = "cart_id") private Long id; @OneToOne @JoinColumn(name = "member_id") private Meber member;
1. @OneToOne 어노테이션을 이용해 Member 엔티티와 일대일로 매핑
2. @JoinColumn 어노테이션을 이용해 매핑할 외래키를 지정
이렇게 설정했을 시, 회원 엔티티에는 장바구니 엔티티와 관련된 소스가 하나도 없고, 장바구니 엔티티가 일방적으로 회원 엔티티를 참조하고 있다. 장바구니 엔티티를 조회하면서, 회원 엔티티의 정보도 가져올 수 있다는 장점이 있다.
!! 즉시로딩과 지연로딩
- 즉시로딩(Fetch.EAGER)
- 엔티티를 조회할 때 해당 엔티티와 매핑된 엔티티를 한 번에 조회하는 것
- 일대일, 다대일로 매핑할 경우 즉시 로딩이 기본 Fetch 전략으로 설정된다
- N+1문제 발생 가능성
- 지연로딩(Fetch=LAZY)
- 실무에서 즉시로딩을 한다면 어떤 쿼리가 실행될지 예측할 수 없음, 불필요한 데이터를 조회 => 성능 하락
- 실제 엔티티 대신에 프록시 객체를 넣어둔다.
- 실제 사용 전까지 데이터 로딩을 하지 않고, 실제 사용 시점에 조회 쿼리문이 실행
- 일대다의 경우 기본 Fetch 전략은 지연로딩
다대일 단방향 연관관계
장바구니 - 장바구니 상품 - 상품 ERD # CartItem Entity @Entity pulbic class CartItem { @Id @Column(name = "cart_item_id") private Long id; # 다대일 관계 매핑 @ManyToOne @JoinColumn(name = "cart_id") private Cart cart; @ManyToOne @JoinColumn(name = "item_id") private Item item; private int count; }
1. 하나의 장바구니에는 여러 개의 상품을 담을 수 있음으로 @ManyToOne 어노테이션을 이용해 다대일 매핑
2. 장바구니에 담을 상품의 정보를 알아야 하므로 상품 엔티티 매핑, 하나의 상품 역시 여러 장바구니의 상품으로 담길 수 있으므로 다대일 매핑
다대일/일대다 양방향 연관관계
양방향 매핑이란 단방향 매핑이 2개 있는 것과 같다.
양방향 매핑으로 연관관계를 매핑하면, 해당 엔티티는 많은 테이블과 연관 관계를 맺게 되고, 엔티티 클래스가 복잡해지기 떼문에 우선 단방향 매핑으로 설계 후, 필요할 경우 추가하는 것이 권장된다.
주문 - 주문 상품 ERD # Order Entity @Entity @Table(name = "orders") public class Order { @Id @Column(name = "order_id") private Long id; @ManyToOne @JoinColumn(name = "member_id") private Member member; private LocalDateTime orderDate; @Enumerated(Enumtype.STRING) private OrderStatus orderStatus; # 연관 관계의 주인 설정 @OneToMany(mappedBy = "order") private List<OrderItem> orderItems = new ArrayList<>(); private LocalDateTime regTime; private LocalDateTime updateTime; } # OrderItem Entity @Entity public class OrderItem { @Id @Column(name = "order_item_id") private Long id; @ManyToOne @JoinColumn(name = "item_id") private Item item; @ManyToOne @JoinColumn(name = "order_id") private Order order; private int orderPrice; private int count; private LocalDateTime regTime; private LocalDateTime updateTime; }
- 한 명의 회원은 여러 번 주문을 할 수 있으므로 주문 엔티티 기준에서 다대일 단방향 매핑
- 하나의 상품은 여러 주문 상품으로 들어갈 수 있으므로 주문 상품 엔티티 기준에서 다대일 단방향 매핑
- 한 번의 주문에 여러 개의 상품을 주문할 수 있으므로 주문 상품 엔티티와 주문 엔티티를 다대일 단방향 매핑 먼저 설정
- 주문 엔티티 기준에서 주문 상품 엔티티와 일대다 매핑, 외래키(order_id)가 order_item 테이블에 있으므로 연관 관계의 주인은 OrderItem 엔티티이다. Order 엔티티가 주인이 아니므로 "mappedBy" 속성으로 연관 관계의 주인을 설정한다.
- 하나의 주문이 여러 개의 주문 상품을 가지므로 List 자료형을 사용해서 매핑을 한다.
!! 양방향 매핑에서는 연관 관계 주인을 설정하는 것이 중요
엔티티를 양방향 연관 관계로 설정하면, 객체의 참조는 둘인데 외래키는 하나이므로 둘 중 외래키 관리 주체를 정해야한다
- 연관 관계의 주인은 외래키가 있는 곳으로 설정
- 연관 관계의 주인이 외래키를 관리(등록, 수정, 삭제)
- 주인이 아닌 쪽은 연관관계 매핑 시 mappedBy 속성의 값으로 연관 관계의 주인을 설정
- 주인이 아닌 쪽은 읽기만 가능
다대다 연관관계
실무에서는 사용하지 않는 매핑 관계이다. 관계형 데이터베이스의 경우, 정규화된 테이블 2개로 다대다를 포현할 수 없다.
따라서, 연결 테이블을 생성해서 일대다,다대일 관계로 풀어낸다.
출처
'Java > Java Spring' 카테고리의 다른 글
도메인 주도 설계란 무엇인가? (1) 2023.05.03 - 즉시로딩(Fetch.EAGER)