Lined Notebook

[백견불여일타 스프링 부트 쇼핑몰] 04. 연관관계 매핑 종류

by ymkim

01. 연관관계 매핑 종류

  1. 엔티티들은 대부분 다른 엔티티와 “연관 관계”를 맺고 있다
  2. 아래 내용을 보면 총 4가지의 연관관계 매핑이 존재
    1. 일대일(1:1): @OneToOne
    2. 일대다(1:N): @OneToMany
    3. 다대일(N:1): @ManyToOne
    4. 다대다(N:N): @ManyToMany
  3. 일대일 매핑의 예시
    1. 쇼핑몰의 회원은 회원 당 하나의 장바구니를 가질 수 있다
    2. 장바구니 입장에서 보아도 자신과 매핑되는 회원은 한명이다
    3. 즉, 회원 엔티티와 장바구니 엔티티는 일대일(1:1)의 관계
  4. 일대다 매핑의 예시
    1. 하나의 장바구니에는 여러개의 상품이 들어갈 수 있음
    2. 즉, 장바구니 엔티티와 장바구니 상품 엔티티는 일대다(1:N) 관계
  5. 두번째로 중요한 부분은 엔티티 매핑 시에는 방향성으로 고려해야 한다
    1. 테이블은 항상 양방향 연관 관계를 가짐 (PK → FK)
    2. 객체는 단방향, 양방향이 존재함

02. 일대일 단방향 매핑하기

  1. 회원 엔티티는 4장에서 만들었기에 장바구니(Cart) 엔티티 생성 후 연관관계 매핑 설정
  2. 회원(1) → 장바구니(1) → 1:1 관계 지정
  3. @OneToOne, @ManyToOne → xxxToOne 으로 시작하면 기본이 즉시 로딩(EAGER 전략)
public class Cart {

		...

		@OneToOne(fetch = FetchType.EAGER)
		@JoinColumn(name = "member_id")
		private Member member;

}
  1. 즉시 로딩보다는, 지연 로딩을 사용하는 것이 좋다라고 기억이 나는데….

https://ict-nroo.tistory.com/132

03. 다대일 단방향 매핑

package com.shop.domain.cartitem.entity;

import com.shop.domain.cart.entity.Cart;
import com.shop.domain.item.entity.Item;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.*;

@ToString
@Getter
@Setter // 실무에서는 사양 지양
@Table(name = "cart_item")
@Entity
public class CartItem {

    /**
     * 장바구니에는 고객이 관심 있거나 나중에 사려는 "상품"을 담아 둘 것이다.
     * 1. 회원(1)이 상품(N)을 여러개 담을 수 있다
     * 2. 하나의 장바구니에는 여러개의 상품이 들어갈 수 있다
     * 3. 같은 상품을 여러 개 주문할 수도 있기에 몇 개를 담아 줄 것인지도 설정해야 함
     */

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "cart_item_id")
    private Long id;

    /* 하나의 "장바구니"에는 여러개의 "상품"을 담을 수 있기에 @ManyToOne 어노테이션 사용 */
    @ManyToOne
    @JoinColumn(name = "cart_id")
    private Cart cart;

    /*
        장바구니에 담을 상품ㅁ의 정보를 알아야 하기에 상품 엔티티 매핑, 하나의 상품은 여러 장바구니의 장바구니 상품으로
        담길 수 있기에 마찬가지로 @ManyToOne 어노테이션 사용
     */
    @ManyToOne
    @JoinColumn(name = "item_id")
    private Item item;

    private int count;
}
  1. Cart, Item 엔티티 사이에 CartItem 엔티티 생성
  2. CartItem 엔티티
    1. Cart의 cart_id를 외래키(FK)로 갖는다
    2. Item의 item_id를 외래키(FK)로 갖는다
  3. 데이터 등록 예시 → 아래와 같이 데이터가 등록 될 수 있을 듯
    1. cart_id : 1(홍길동의 장바구니), item_id : 4(곰돌이), count : 3
    2. cart_id : 1(홍길동의 장바구니), item_id : 2(장갑), count : 4
    3. cart_id : 1(홍길동의 장바구니), item_id : 7(죽부인), count : 2

04. 다대일/일대다 양방향 매핑

  1. 양방향 매핑이란 단방향 매핑이 2개 있는 것이라 생각 하면 됨
  2. 웬만하면 단방향 연관관계 구조를 갖도록 설계를 해야한다
    1. 양방향 연관관계 사용 시 양쪽 참조가 가능하므로 조회가 간편함
    2. 하지만 구조가 복잡해지면 양방향 연관관계 사용에 대한 논리적 오류 발생 가능

05. 다대다 매핑

  1. 실무에서 다대다 연관관계 매핑은 사용하지 않음
  2. RDBMS(관계형 DB)에서는 정규화된 2개의 테이블을 다대다(N:N)로 표현할 수 없음
  3. 따라서 연결 테이블을 생성해서 다대다 관계를 일대다(1:N), 다대일(N:1)로 표현해야 함
  4. 객체는 테이블과 다르게 컬렉션을 사용해서 다대다 관계 표현 가능
    1. Member Entity
      1. List_items
    2. Item Entity
      1. List_memebers
  5. 현재 프로젝트 내에서 다대다 매핑은 사용하지 않지만 코드로 간략히 표현
public class Item {
		
		@ManyToMany
		@JoinTable(
					name = "member_item",
					joinColumns = @JoinColumn(name = "member_id"),
					inverseJoinColumns = @JoinColumn(name = "item_id")
		)
		private List<Member> members;
}
  1. 다대다 매핑을 사용하지 않는 이유
    1. 연결 테이블에는 컬럼 추가가 불가능
    2. 연결 테이블에는 조인 컬럼뿐만 아니라 추가 컬럼들이 필요한 경우가 많음
    3. 또한 엔티티 조회 시 member 엔티티에서 item을 조회하면 중간 테이블이 있기에 어떤 쿼리가 실행될지 예측하기 쉽지 않음
    4. 따라서 연결 테이블용 엔티티 하나 생성 후 일대다, 다대일 관계로 표현하면 됨

06. 영속성 전이

  1. 영속성 전이란 엔티티의 상태를 변경할 때 해당 엔티티와 관련된 엔티티의 상태 변화를 전파시키는 옵션이다.
  2. 이때 부모는 One에 해당, 자식은 Many에 해당한다
  3. 예로 Order 엔티티 삭제 시 OrderItem 엔티티가 함께 삭제 되거나, Order 엔티티를 저장 할 때 Order 엔티티에 담겨있던 OrderItem 엔티티를 한꺼번에 저장할 수 있다

CASCADE 종류 설명

PERSIST 부모 엔티티가 영속화될 때 자식 엔티티도 영속화
MERGE 부모 엔티티가 병합될 때 자식 엔티티도 병합
REMOVE 부모 엔티티가 삭제될 때 연관된 자식 엔티티도 삭제
REFRESH 부모 엔티티가 refresh 되면 자식 엔티티도 refresh
DETACH 부모 엔티티가 detach 되면 자식 엔티티도 detach
ALL 부모 엔티티의 영속성 상태 변화를 자식 엔티티에 모두 전이

07. 고아 객체

  1. 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 고아 객체라함
  2. 영속성 전이 기능 사용하면 부모 엔티티를 통해 자식의 생명주기 관리 가능
  3. 고아 객체 기능은 참조하는 곳이 하나일 때 사용해야 함
  4. @OneToOne(일대일), @OneToMany(일대다) 에서 사용

08. 지연 로딩

  1. 엔티티 조회 시 연관된 엔티티를 함께 조회하는 즉시로딩을 알아보자
  2. 즉시 로딩 이외에도 지연 로딩이라는 Fetch 전략이 존재

블로그의 정보

기록하고, 복기하고

ymkim

활동하기