도메인 모델과 테이블 설계
회원, 주문, 상품의 관계
- 회원은 여러 상품을 주문 가능하고, 한 번 주문할 때 여러 상품을 선택할 수 있으므로 주문과 상품은 다대다 관계이다.
- 하지만, 다대다 관계는 거의 사용하지 않아서 주문 상품이라는 엔티티를 추가해서 다대다 관계를 일대다, 다대일 관계로 풀어낸다.
회원 엔티티 분석
- Member
- 이름과 임베디드 타입인 Address, 주문을 리스트로 가진다.
- Order
- 한 번 주문시 여러 Item을 담을 수 있어서 주문(Order)과 주문 상품(OrderItem)은 일대다 관계를 가진다.
- 상품을 주문한 회원, 배송 정보, 주문 날짜, 주문 상태를 가짐.
- 주문 상태는 열거형으로 ORDER, CANCEL을 표현할 수 있음.
- OrderItem
- 주문한 상품 정보와 주문 금액과 주문 수량을 가지고 있음.
- Item
- 이름, 가격, 재고 수량, 카테고리를 리스트로 가지고 있음.
- 상품을 주문하면 재고 수량이 줄어들도록 해야함.
- 회원이 주문을 하는 것이라서 회원이 주문 리스트를 가지는게 괜찮은 것 같지만, 실무에서는 주문이 회원을 참조하는 것으로 충분하다.
회원 테이블 분석
연관 관계 매핑 분석
- 회원과 주문: 일대다, 다대일의 양방향 관계. 연관관계의 주인을 정해야 하는데, 외래키가 있는 주문을 연관 관계의 주인으로 정하는게 좋다. 그러므로 Order.member를 ORDERS.MEMBER_ID 외래키와 매핑한다.
- 주문 상품과 주문: 다대일의 양방향 관계. 외래 키가 주문 상품에 있으므로 주문 상품이 연관 관계의 주인이다. 그러므로 OrderItem.order를 ORDER_ITEM.ORDER_ID 외래 키와 매핑한다.
- 주문 상품과 상품: 다대일의 단방향 관계. OrderItem.item을 ORDER_ITEM.ITEM_ID 외래키와 매핑한다.
- 주문과 배송: 일대일 단방향 관계. Order.delivery를 ORDERS.DELIVERY_ID 외래 키와 매핑한다.
- 카테고리와 상품: @ManyToMany를 사용해서 매핑한다. (실무에서는 @ManyToMany를 사용하면 X)
연관 관계의 주인은 외래키를 누가 관리하냐의 문제이지 비즈니스 상 누가 우위에 있느냐가 아님.
실무에서는 가급적 Getter는 열어두고, Setter는 꼭 필요한 경우에만 사용하는 것을 추천. Getter는 아무리 호출해도 호출하는 것만으로는 어떤 일이 발생하지 않지만, Setter는 호출하면 데이터가 변하므로, 막 열어두면 엔티티가 왜 변경되는지 추적하기 힘들어진다. 엔티티를 변경할 때는 Setter를 사용하는 것 대신에 명확한 변경 지점을 알 수 있도록 비즈니스 메서드를 별도로 제공해야한다.
엔티티 설계시 주의점
- 엔티티에는 가급적 Setter를 사용하지 말자
- 모든 연관 관계는 지연 로딩으로 설정
- 즉시 로딩(EAGER)은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다.
- 즉시 로딩: Member를 조회할 때 연관된 Order를 한번에 조회하는 것. 엔티티를 로딩할 때 필요한 다른 애들도 모두 로딩.
- 하나를 로딩하려하는데 잘못하면 모든 애들이 로딩될 수도.
- 실무에서 모든 연관 관계는 지연 로딩(LAZY)로 설정해야 한다.
- 연관된 엔티리를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.
- XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시 로딩이라서 지연 로딩으로 직접 설정해줘야 한다.
- 즉시 로딩(EAGER)은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다.
- 컬렉션은 필드에서 초기화 하자.
- null문제에서 안전
- ex) private List<Order> orders = new ArrayList<>();
- Cascade
- 모든 엔티티는 저장하고 싶으면 기본적으로 persist를 각자 해줘야 하는데 cascade 값만 설정해주면 같이 persist 호출이 된다.
- 아래 코드에서 Order를 persist하면 OrderItems와 Delivery도 같이 persist를 호출해준다.
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
...
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "delivery_id")
private Delivery delivery;
...
}
728x90
'Spring' 카테고리의 다른 글
[Springboot & JPA 1] 상품 도메인 개발 (0) | 2024.05.03 |
---|---|
[Spring boot & JPA 1] 회원 도메인 개발 (0) | 2024.05.01 |
[Spring DB2] 스프링 트랜잭션 전파2 - 활용 (0) | 2024.03.31 |
[Spring DB2] 스프링 트랜잭션 전파1 - 기본 (0) | 2024.03.31 |
[Spring DB2] 스프링 트랜잭션 이해 - 2 (0) | 2024.03.28 |