[JPA 활용1][2] 도메인 분석 설계

kiteB·2021년 10월 25일
0

JPA

목록 보기
14/28
post-thumbnail

[ 요구사항 분석 ]

📌 기능 목록


[ 도메인 모델과 테이블 설계 ]

회원, 주문, 상품의 관계

  • 회원은 여러 상품을 주문할 수 있다.
    그리고 한 번 주문할 때 여러 상품을 선택할 수 있으므로 주문과 상품은 다대다 관계이다.
  • 하지만 이런 다대다 관계는 관계형 데이터베이스는 물론이고 엔티티에서도 거의 사용하지 않는다.
    주문상품이라는 엔티티를 추가해서 다대다 관계를 일대다, 다대일 관계로 풀어낸다.

상품 분류

  • 상품은 도서, 음반, 영화로 구분되는데 상품이라는 공통 속성을 사용하므로 상속 구조로 표현했다.

1. 회원 엔티티 분석

🚫 주의

실무에서는 회원이 주문리스트를 가지도록 설계하지 말자 ❗
즉, 다대다 관계를 사용하면 안된다!

  • 가급적이면 양방향 연관관계보다는 단방향 연관관계를 사용하자!
    • 실무에서는 회원이 주문을 참조하지 않고, 주문이 회원을 참조하는 것으로 충분하다.
      주문을 생성할 때 회원이 필요하기 때문이다!
  • 지금은 MemberorderList를 가지도록 설계했다.
    • 이는 다대다 관계로 설계한 것인데, 다대다 대신 1:다 & 다:1로 풀어내야 한다.
    • 여기서는 일대다, 다대일의 양방향 연관관계를 설명하기 위해서 추가한 것으로, 실제로는 MemberorderList는 필요없다!!

2. 회원 테이블 분석

🔗 엔티티와 테이블의 차이

MEMBER

  • 회원 엔티티의 Address 임베디드 타입 정보가 회원 테이블에 그대로 들어갔다. DELIVERY 테이블도 마찬가지!

ITEM

  • 앨범, 도서, 영화 타입을 통합해서 하나의 테이블로 만들었다.
  • DTYPE 컬럼으로 타입을 구분한다.

📌 참고: DB명

실제 코드에서는 DB에 소문자 + _ 스타일을 사용할 것이다!


3. 연관관계 매핑 분석

회원과 주문

  • 일대다, 다대일의 양방향 관계
  • 연관관계의 주인을 정해야 하는데, 외래 키가 있는 주문을 연관관계의 주인으로 정하는 것이 좋다.
    Order.memberORDERS.MEMBER_ID 외래 키와 매핑한다.

주문상품과 주문

  • 다대일 양방향 관계
  • 외래 키가 주문상품에 있으므로 주문상품이 연관관계의 주인이다.
    OrderItem.orderORDER_ITEM.ORDER_ID 외래 키와 매핑한다.

주문상품과 상품

  • 다대일 단방향 관계
  • OrderItem.itemORDER_ITEM.ITEM_ID 외래 키와 매핑한다.

주문상품과 주문

  • 일대일 양방향 관계
  • Order.deliveryORDERS.DELIVERY_ID 외래 키와 매핑한다.

카테고리와 상품

  • @ManyToMany를 사용해서 매핑한다.
  • 실무에서 @ManyToMany 사용 금지❗ 여기서는 다대다 관계를 예제로 보여주기 위해 추가한 것!

⭐ 외래 키가 있는 곳이 연관관계의 주인 ❗

연관관계의 주인은 단순히 외래 키를 누가 관리하냐의 문제이다.
그러므로 비즈니스상 우위에 있는 것을 주인으로 정하면 안 된다.


[ 엔티티 클래스 개발 ]

  • 예제에서는 설명을 쉽게하기 위해 엔티티 클래스에 Getter, Setter를 모두 열어두고 최대한 단순하게 설계
  • 실무에서는 가급적 Getter를 열어두고, Setter는 꼭 필요한 경우에만 사용하자!

🔗 전체 코드 확인하기

실행 결과

[ 엔티티 설계시 주의점 ]

1. 엔티티에는 가급적 Setter를 사용하지 말자!

  • Setter가 열려 있으면 변경 포인트가 너무 많아서 유지보수가 어렵다.

2. 모든 연관관계는 지연로딩으로 설정! (LAZY) ⭐

  • 즉시로딩(EAGER)은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다.
    • 특히 JPQL을 실행할 때 N + 1 문제가 자주 발생한다!
    • N + 1 문제란?

모든 연관관계는 LAZY로 설정하자.

  • 연관된 엔티티를 함께 DB에서 조회해야 하면,
    fetch join 또는 엔티티 그래프 기능을 사용한다.
  • @XToOne은 기본값이 EAGER이므로 LAZY로 직접 설정해줘야 한다.

3. 컬렉션은 필드에서 초기화하자!

  • 컬렉션은 필드에서 바로 초기화하는 것이 안전하다.
    • null 문제에서 안전
    • 하이버네이트는 엔티티를 영속화할 때,
      컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다.
      → 만약 getOrders()처럼 임의의 메서드에서 컬렉션을 잘못 생성하면 하이버네이트 내부 매커니즘에 문제가 발생할 수 있다.

4. 테이블, 컬럼명 생성 전략

스프링부트에서 하이버네이트 기본 매핑 전략을 변경해서 실제 테이블 필드명은 다르다.

  • 하이버네이트 기존 구현: 엔티티의 필드명을 그대로 테이블의 컬럼명으로 사용
  • 스프링 부트 신규 설정 (엔티티(필드) → 테이블(컬럼))
    1. 카멜 케이스 → 언더스코어(memberPointmember_point)
    2. ._
    3. 대문자 → 소문자
profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글