[Spring Data] JPA - Relationship Mapping

Seonghun Kim·2022년 9월 5일
0

Spring!

목록 보기
5/8
post-thumbnail

📌 Relationship mapping

  • entity에 연관 관계를 설정하여 객체지향적으로 프로그래밍할 수 있도록 함
  • entity의 관계를 일대일, 일대다, 다대일 등으로 매핑
  • 연관 관계의 방향과 연관 관계의 주인을 고려하여 설정

연관 관계의 방향

  • 단방향과 양방향으로 분류
  • 참조가 꼭 필요한 경우들을 고려하며 설정

연관 관계의 주인

  • 두 entity가 양방향 관계일 경우, 연관 관계의 주인이 필요
  • 연관 관계의 주인은 외래키를 저장, 수정, 삭제하는 등의 관리 권한을 가짐
  • 주인인 아닌 entity는 조회만 가능하며, mappedBy 속성을 사용하여 표현
  • 일반적으로 외래키가 있는 entity를 주인으로 설정

✔ 일대일 단방향 매핑

  • 쇼핑몰에서의 회원(member)과 장바구니(cart)의 관계를 설정
  • cart의 경우 어떤 member를 주인으로 하는지 항상 필요하므로, member의 id를 외래키로 가지며 연관 관계의 주인이 됨

member entity

@Entity
@Table(name="member")
public class Member {

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

    private String name;

    @Column(unique = true)
    private String email;

    private String password;

    private String address;

    @Enumerated(EnumType.STRING)
    private Role role;
    
}

cart entity

@Entity
@Table(name = "cart")
public class Cart {

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

    @OneToOne
    @JoinColumn(name="member_id")
    private Member member;

}
  • 연관 관계의 주인인 entity에서 @OneToOne으로 관계 종류를 표현하고, @JoinColumn으로 컬럼의 이름을 지정하여 매핑

✔ 다대일 단방향 매핑

  • 쇼핑몰에서의 장바구니(cart)와 상품(item)의 관계를 설정
  • cart의 경우 여러 item을 포함할 수 있으며, item 또한 여러 cart에 포함될 수 있음
  • 이는 다대다 매핑으로 표현할 수 있지만, cart_itemcount와 같은 컬럼을 추가할 수 없기 떄문에 위와 같이 일대다, 다대일 관계로 풀어서 표현하는 것이 일반적
  • cart_itemcart의 id와 item의 id를 모두 외래키로하여 두 연관 관계에서 모두 주인이 됨

item entity

@Entity
@Table(name = "item")
public class Item {

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

    @Column(nullable = false, length = 50)
    private String itemNm;

    @Column(name = "price", nullable = false)
    private int price;

    @Column(nullable = false)
    private int stockNumber;

    @Lob
    @Column(nullable = false)
    private String itemDetail;

    @Enumerated(EnumType.STRING)
    private ItemSellStatus itemSellStatus;

}

cart_item entity

@Entity
@Table(name = "cart_item")
public class CartItem {

    @Id
    @GeneratedValue
    @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;

}
  • 연관 관계의 주인인 entity에서 @ManyToOne으로 관계 종류를 표현하고, @JoinColumn으로 컬럼의 이름을 지정하여 매핑

✔ 다대일 양방향 매핑

  • 쇼핑몰에서의 주문(order)과 주문 상품(order_item) 관계를 설정
  • order에는 여러개의 order_item이 포함될 수 있으므로 다대일 관계로 매핑
  • 연관 관계의 주인인 order_item이 어떤 order에 속하는지 뿐만 아니라, order에서 어떤 order_item들이 있는지 참조가 필요한 경우에는 양방향 매핑 필요

order_item entity

@Entity
public class OrderItem {

    @Id @GeneratedValue
    @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;

}

order entity

@Entity
@Table(name = "order")
public class Order {

    @Id @GeneratedValue
    @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<>();

}
  • 연관 관계의 주인이 아닌 entity에서 @OneToMany로 관계 종류를 표현하고, mappedBy 속성으로 연관 관계 주인 entity의 컬럼을 설정
  • 하나의 order에는 여러개의 order_item이 매핑되므로 List 자료형으로 선언

📌 Cascade

영속성 전이 (cascade)

  • entity의 상태를 변경할 때, 해당 entity와 연관된 entity의 상태 변화를 전파시키는 옵션
  • 단일 entity에 종속적이고 부모 entity와 자식 entity의 life cycle이 유사할 때 활용하는 것이 일반적
  • PERSIST : 부모 entity가 영속화되면 자식 entity도 영속화
  • MERGE : 부모 entity가 병합되면 자식 entity도 병합
  • REMOVE : 부모 entity가 삭제되면 자식 entity도 삭제
  • REFRESH : 부모 entity가 새로고침되면 자식 entity도 새로고침
  • DETACH : 부모 entity가 detach되면 자식 entity도 detach
  • ALL : 부모 entity가 영속성 상태 변화를 자식 entity에 모두 전이

order entity

...
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> orderItems = new ArrayList<>();
...

✔ 영속성 전이

  • cascade = CascadeType.ALL
  • order entity의 영속성 상태 변화를 order_item entity에 모두 전이
  • @OneToMany 옵션으로 사용

✔ 고아 객체 제거

  • orphanRemoval = true
  • CascadeType.REMOVE의 경우 부모 entity가 삭제되면 모든 연결된 자식 entity가 제거되지만, 부모 entity에서 자식 entity를 제거하면 자식 entity는 그대로 남아있는 현상이 발생 (고아 객체)
  • 이를 해결하기 위해 orphanRemoval 옵션을 설정
  • 영속성 전이 기능과 같이 사용하면 부모 entity를 통해서 자식 entity의 생명 주기를 관리할 수 있음
  • 자식 entity를 오직 부모 entity에서만 참조하는 경우 사용 가능
  • @OneToOne 또는 @OneToMany 옵션으로 사용

📌 Lazy Loading

✔ 즉시 로딩 (Eager looading)

엔티티를 조회할 때 연관된 엔티티도 함께 조회하는 방식

  • fetch = FetchType.EAGER
  • JPA에서는 연관된 객체까지 조회하려고 쿼리를 더 실행하기 보다 즉시 로딩을 최적화하기 위해 가능하면 조인 쿼리를 사용
  • @ManyToOne, @OneToOne 과 같이 연관된 entity가 하나인 경우의 기본 로딩 방식
  • 매핑되는 entity의 개수는 많으면 쿼리를 예측하기 어렵고, 사용하지 않는 데이터도 한번에 조회하여 성능 문제도 발생하기 때문에 실무에서는 사용하기 어려움

✔ 지연 로딩 (LAZY LOADING)

연관된 엔티티를 실제 사용하는 시점에 데이터베이스에서 조회하는 방식

  • fetch = FetchType.LAZY
  • 연관된 객체에 프록시 객체를 넣어두고, 실제 데이터가 필요한 순간이 되면 데이터베이스를 조회하여 프록시 객체를 초기화
  • @OneToMany, @ManyToMany와 같이 연관된 entity가 여러개인 경우의 기본 로딩 방식

cart_item entity

...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="cart_id")
private Cart cart;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
...
  • cart_item을 조회하였을 때 불필요하게 cart, item을 조회하지 않도록 설정

0개의 댓글