twocowsong

[실전 예제] 요구사항 분석과 기본 매핑 본문

IT/JPA

[실전 예제] 요구사항 분석과 기본 매핑

WsCode 2022. 5. 22. 21:31

깃허브 정리 URL : https://github.com/sWineTake/jpa.git

 

GitHub - sWineTake/jpa: 자바 ORM 표준 JPA 프로그래밍 - 김영한

자바 ORM 표준 JPA 프로그래밍 - 김영한. Contribute to sWineTake/jpa development by creating an account on GitHub.

github.com

직접 코딩하면서 예제를 순서대로 따라오는것을 권장합니다.

먼저 요구사항을 분석하고 도메인 모델과 테이블을 설계합니다.

 

요구사항 분석

- 회원은 상품을 주문할 수 있습니다.

- 주문 시 여러 종류의 상품을 선택할 수 있습니다.

 

요구사항을 분석해서 만든 메인화면 기능은 아래와같습니다.

- 회원기능 : 회원 등록, 회원 조회

- 상품기능 : 상품 등록, 상품 수정, 상품 조회

- 주문기능 : 상품 주문, 주문 내역 조회, 주문 취소

 

요구사항을 분석해보니 회원, 주문 상품 그리고 주문 상품이라는 엔티티가 도출되었습니다.

 

회원과 주문의 관계 : 회원은 여러번의 주문이 가능함으로 1 : N 관계입니다.

주문과 상품의 관계 : 주문할 때 여러 상품을 함께 선택할 수 있고, 같은 상품도 여러 번 주문될 수 있으므로 둘은 N : M 관계입니다. 하지만 이런 N : M 관계는 관계형 DB에서 물론이고 엔티티에서도 거의 사용하지않습니다.

따라서 주문상품이라는 연결 엔티티를 추가해서 N : M 관계를 1 : 1 , 1 : N 관계로 풀어냈습니다.

주문 상품에는 해당 상품을 구매한 금액과 수량 정보가 포함되어있습니다.

 

요구사항을 기반으로 테이블이 도출되었습니다.

엔티티를 생성해보겠습니다.

@Getter
@Setter
@Table(name = "MEMBER")
public class Member {

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

   @Column
   private String name;

   @Column
   private String city;

   @Column
   private String street;

   @Column
   private String zipcode;

}
@Entity
@Table(name = "ORDERS")
@Getter
@Setter
public class Order {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "ORDER_ID")
   private Long id;

   @Column(name = "MEMBER_ID")
   private Long memberId;

   @Temporal(TemporalType.TIMESTAMP)
   private Date orderDate;

   @Enumerated(EnumType.STRING)
   private OrderStatus orderStatus;
}
public enum OrderStatus {
   ORDER, CANCEL
}
@Entity
@Getter
@Setter
@Table(name = "ORDER_ITEM")
public class OrderItem {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "ORDER_ITEM_ID")
   private Long id;

   @Column(name = "ORDER_ID")
   private Long orderId;

   @Column(name = "ITEM_ID")
   private Long itemId;

   @Column(name = "ORDER_PRICE")
   private int orderPrice;

   private int count;
}
@Entity
@Setter
@Getter
@Table(name = "ITEM")
public class Item {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "ITEM_ID")
   private Long id;

   private String name;

   private int price;

   @Column(name = "STOCK_QUANTITY")
   private int stockQuantity;
}

서적에서는 @GeneratedValue의 기본값인 AUTO로 생성하였지만 저는 GenerationType.IDENTITY로 생성하였습니다.

 

지금 이 방식은 객체 설계를 테이블 설계에 맞춘 방법입니다. 

특히 테이블의 오래키를 객체에 그대로 가져온 부분이 문제입니다.

왜냐하면 관계형 DB는 연관 된 객체를 찾을 때 외래 키를 사용해서 조인하면 되지만 객체에는 조인이라는 기능이 없습니다. 객체는 연관된 객체를 찾을 때 참조를 사용해야 합니다.

 

객체에서 참조 대신에 DB의 외래키를 그대로 가지고 있으므로 order.getMember() 처럼 객체 그래프를 탐색할 수 없고 객체의 특성도 살릴 수 없습니다. 

이렇게 외래키만 가지고 있으면 연관된 엔티티를 찾을 때 외래키로 DB를 다시 조회해야합니다.

예를들어 주문을 조회한 다음 주문과 연관된 호원을 조회하려면 다음처럼 외래키를 사용해서 다시 조회해야합니다.

String orderId = "orderId";
// 주문 번호로 주문 정보 조회
Order order = em.find(Order.class, orderId);

// 외래키로 다시 회원 정보 조회
Member member = em.find(Member.class, order.getMemberId());

객체는 참조를 사용해서 연관관계를 조회할 수 있습니다.

따라서 다음처럼 참조를 사용하는것이 객체지향적인 방법입니다.

String orderId = "orderId";
// 주문 번호로 주문 정보 조회
Order order = em.find(Order.class, orderId);
Member member = order.getMember();

 

정리하자면 객체는 참조를 사용해서 연관된 객체를 찾고 테이블은 외래키를 사용해서 연관된 테이블을 찾으므로 둘 사이에는 큰 차이가 있습니다.

 

JPA는 객체의 참조와 테이블의 외래키를 매핑해서 객체에서는 참조를 사용하고 테이블에서는 외래키를 사용할 수 있도록 합니다. 이제부터 참조와 외래키를 매핑하는방법을 천천히 알아보겠습니다.

'IT > JPA' 카테고리의 다른 글

객체 연관관계와 테이블의 연관관계  (0) 2022.05.25
연관관계 매핑 기초  (0) 2022.05.23
중간 정리  (0) 2022.05.21
@Transient  (0) 2022.05.21
@Lob  (0) 2022.05.21