Programming-[Backend]/JPA

[JPA기본] 8. 연관, 상속관계 매핑 실습 : 테이블 -> 엔티티 환원

컴퓨터 탐험가 찰리 2021. 10. 13. 21:54
728x90
반응형

 

1. 테이블 설계도

이때까지 배운 내용을 바탕으로, 테이블 설계 내용에 따라 Entity를 직접 작성해본다. 테이블의 설계도만 보고 이때까지 배운 내용대로 Entity들을 작성할 수 있어야 한다. 실제로 그렇게 해보고, 안되는 부분이 있다면 아래 내용들을 참고한다. 강의에서 나온 테이블 설계도는 다음과 같다.

 


 

2. 단방향 연관관계 : ORDERS, MEMBER, DELIVERY

Member와 Orders는 1:N의 관계이고, Orders와 Delivery는 1:1의 관계이다. Orders에서 Member를 여러 개 가지므로 Member_id를 FK로 갖는다. 그렇기 때문에 Orders가 연관관계의 주인이다.

단방향 연관관계로 설정된 이유는 (아마도) 비즈니스 로직상 Delivery 정보에서 Order 정보로 넘어갈 일이 없기 때문이다. 다시 말해 배송 정보를 기반으로 주문 정보가 어떤지 찾아낼 일은 없다고 판단한 것 같다. 또한 Member 정보에서 Order 정보로 넘어가는 일도 없다고 기획을 한 것 같다.

@Enumerated로 ENUM 방식을 활용하여 status를 정의한 것도 살펴보자. 배운바와 같이 EnumType.ORDINAL은 사용하지 않고 EnumType.STRING을 사용하였다.

src/main/java/com/example/jpamain/domain/Member.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
@Getter
@Setter
public class Member {
 
  @Id
  @GeneratedValue
  private Long id;
 
  private String name;
 
  private String city;
 
  private String street;
 
  private Integer zipcode;
}
cs

 


src/main/java/com/example/jpamain/domain/Delivery.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
@Getter
@Setter
public class Delivery {
 
  @Id
  @GeneratedValue
  private Long id;
 
  private String city;
 
  private String street;
 
  private String zipcode;
 
  private String status;
}
cs

 



src/main/java/com/example/jpamain/domain/Order.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Entity
@Table(name = "ORDERS")
@Getter
@Setter
public class Order {
 
  @Id
  @GeneratedValue
  private Long id;
 
  @ManyToOne
  @JoinColumn(name = "MEMBER_ID")
  private Member member;
 
  @OneToOne
  @JoinColumn(name = "DELIVERY_ID")
  private Delivery delivery;
 
  @OneToMany(mappedBy = "order")
  private List<OrderItem> orderItems = new ArrayList<>();
 
  private LocalDateTime orderDate;
 
  @Enumerated(EnumType.STRING)
  private OrderStatus status;
 
  public void addOrderItem(OrderItem orderItem) {
    orderItems.add(orderItem);
    orderItem.setOrder(this);
  }
 
}
cs

 

 


 

3. 양방향 연관관계 : ORDERS, ORDER_ITEM

 

주문에서도 주문 상품을 조회하고, 주문 상품에서도 어떤 주문에 의한 것인지 조회하기 위해 양방향 연관관계를 맺는다. 1:N 에서 N인 OrderItem이 연관관계의 주인이며, @ManyToOne으로 FK를 갖는다. Order에서는 @OneToMany를 적용하였다.

연관관계 보조 메서드인 addOrderItem을 Order객체에 생성해주었다. Order 생성 시 해당 메서드를 불러와서 추가를 원하는 orderItem을 Order의 orderItems에 add해주고, 반대편 연관관계를 갖는 객체인 orderItem에는 해당 Order 객체를 this를 이용하여 set 해준다.


src/main/java/com/example/jpamain/domain/OrderItem.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Getter
@Setter
public class OrderItem {
 
  @Id
  @GeneratedValue
  private Long id;
 
  @ManyToOne
  @JoinColumn(name = "order_id")
  private Order order;
 
  @ManyToOne
  @JoinColumn(name = "item_id")
  private Item item;
 
  private Integer orderPrice;
 
  private Integer count;
 
}
cs

 



src/main/java/com/example/jpamain/domain/Order.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Entity
@Table(name = "ORDERS")
@Getter
@Setter
public class Order {
 
  @Id
  @GeneratedValue
  private Long id;
 
  @ManyToOne
  @JoinColumn(name = "MEMBER_ID")
  private Member member;
 
  @OneToOne
  @JoinColumn(name = "DELIVERY_ID")
  private Delivery delivery;
 
  @OneToMany(mappedBy = "order")
  private List<OrderItem> orderItems = new ArrayList<>();
 
  private LocalDateTime orderDate;
 
  @Enumerated(EnumType.STRING)
  private OrderStatus status;
 
  public void addOrderItem(OrderItem orderItem) {
    orderItems.add(orderItem);
    orderItem.setOrder(this);
  }
 
}
cs

 

 


 

4. 다대다 연관관계 : ORDERS, ORDER_ITEM, ITEM

 


Orders와 Item는 다대다 연관관계이다. 그러나 @ManyToMany는 안티패턴이기 때문에, 중간 조인 테이블인 ORDER_ITEM으로 다대다 관계를 처리하였다. 이렇게 하기 위해서, 위에서 다룬 바와 같이 조인 테이블인 OrderItem을 하나의 엔티티로 생성해주었다.

@ManyToMany를 사용하면 안되는 이유

애초에 관계형 데이터베이스가 2개의 테이블로는 다대다 관계를 표현할 수 없다. 위의 도표를 예시로 들어서, Order가 여러개의 Item을 갖는다면 Order ID 1번에 Item ID가 1번, 2번, 3번 등 여러 개가 등록될 수 있을 것이다. 반대로 Item이 여러개의 Order를 갖는다면 반대로 등록이 가능하다. 그러나, 양쪽에 동시에 이런 행들을 만들어내는 것은 불가능하다. 관계형 데이터베이스에서 테이블의 1개 행에 List값을 넣을 수 없기 때문이다.

그러나, 객체는 List 등의 컬렉션을 사용하여 다대다 관계가 가능하다. 그래서, @ManyToMany와 @JoinTable(name="원하는 조인 테이블 이름")으로 테이블 생성이 가능하다(반대편에는 @ManyToMany(mappedBy="필드명"을 지정해주어야함). 그러나, 조인테이블이 단순히 두 테이블을 연결하는 매핑용 속성값만(OrderId, ItemId)를 갖는 것이 아니다. 실무에서는 orderAmount, count, date 등 추가 필드가 무수히 들어가기 때문에, @ManyToMany는 실무에서 사용하지 않는다. 게다가 쿼리가 복잡하게 나가는 문제도 있기 때문에 사용을 권장하지 않는다.


src/main/java/com/example/jpamain/domain/Item.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
@Getter
@Setter
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public abstract class Item extends BaseEntity {
 
  @Id
  @GeneratedValue
  private Long id;
 
  private String name;
  private int price;
  private int stockQuantity;
 
  @OneToMany(mappedBy = "item")
  private List<CategoryItem> categoryItems = new ArrayList<>();
 
}
cs

 


그리고, 이전 글에서 공부한 바와 같이 Item 테이블의 속성값을 Book, Movie, Album 등에서 상속받아서 공통으로 사용하기 때문에 상속 매핑을 적용하였으며, @Inheritance, @DiscriminatorColumn을 적용한다. 전체 엔티티에서 공통 속성을 받아내기 위해 BaseEntity를 @MappedSuperclass로 상속하였다.


 

참조

 

1. 인프런_자바 ORM 표준 JPA 프로그래밍 - 기본편_김영한 님 강의

https://www.inflearn.com/course/ORM-JPA-Basic/lecture/21735?tab=curriculum

728x90
반응형