1-2. 도메인 분석 설계

지니🧸·2023년 1월 31일
0

Spring Boot & JPA

목록 보기
2/35

본 문서는 인프런의 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 (김영한) 강의를 공부하며 작성한 개인 노트입니다.

♻️ 도메인 모델과 테이블 설계

  • 회원은 상품 여러개 주문할 수 있음
  • 한 주문에 여러 상품 포함 가능
  • 상품 - 상속구조
  • 다대다 관계는 운영에 사용하면 안됨 > 일대다 관계로 풀어내야 함
  • 양방향 연관관계는 지양하자 > 설계단계에는 단방향 연관관계가 좋음
  • 객체에서는 다대다 관계가 가능하지만 관계형 데이터베이스에서는 불가능하기 때문에 맵핑 테이블이 필요함

연관관계 매핑 분석

  • 회원과 주문: 양방향. 일대다, 다대일.
    • 외래키가 있는 주문을 연관관계의 주인으로 정하자 & 일대다에서 다 = Foreign key 포함
      • (예) Order.member를 ORDERS.MEMBER_ID 외래키와 매핑
  • 주문상품과 주문: 양방향. 다대일.
    • OrderItem.order를 ORDER_ITEM.ORDER_ID 외래키와 매핑
  • 주문상품과 상품: 단방향. 다대일.
    • OrderItem.item을 ORDER_ITEM.ITEM_ID 외래키와 매핑
  • 주문과 배송: 양방향. 일대일
    • Order.delivery를 ORDERS.DELIVER_ID 외래키와 매핑
  • 카테고리와 상품: @ManyToMany

🐷 엔티티 클래스 개발

  • 엔티티 클래스는 domain 패키지 안에 모두

src/main/java/jpabook.jpashop/domain/Member

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String name;

    private Address address;
    
	@OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();
}
  • @Column(name = "member_id") : column 이름 설정
    • 이 에노테이션 없이는 column 이름이 자동으로 변수 이름으로 설정됨
  • @OneToMany(mappedBy = "member") : 나는 orders 테이블의 member 필드에 의해 매핑된 것이다
    • 나는 이 연관관계의 주인이 아님

src/main/java/jpabook.jpashop/domain/Member

@Embeddable
@Getter @Setter
public class Address {

    private String city;
    private String street;
    private String zipcode;

}
  • JPA 내장타입임
  • Imbeddable

src/main/java/jpabook.jpashop/domain/Order

@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
    @Id @GeneratedValue
    @Column(name = "order_id")
    private Long id;

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

}
  • @JoinColumn(name = "member_id") > foreign key 이름이 member_id로 매핑

src/main/java/jpabook.jpashop/domain/Delivery

@Entity
@Getter @Setter
public class Delivery {

    @Id @GeneratedValue
    @Column(name = "delivery_id")
    private Long id;

    private Order order;

    @Embedded
    private Address address;
    
    @Enumerated(EnumType.STRING)
    private DeliveryStatus status;
}
  • @Enumerated : enum class에 항상 표시
    • EnumType - STRING & ORDINAL
      • ORDINAL은 값을 새로 추가하면 순서가 바뀌어 유지가 어려움

주의사항

  • 이론적으로는 Getter & Setter 모두 제공하지 않는 것이 이상적임

    • 실무에서는 Getter는 열어두는 것이 편리
    • Setter는 변경 지점이 명확하도록 비즈니스 메소드로 대체하는 것이 좋음
  • Address 타입

    • 값 타입은 변경 불가능하게 설계 > @Setter 제거
    • JPA 스펙상 @Embeddable의 default constructor는 public/protected여야 함
      • JPA 구현 라이브러리가 객체를 생성할 때 리플렉션 같은 기술을 사용할 수 있도록

🦋 엔티티 설계시 주의점

1. 엔티티에는 Setter 사용하지 말자
2. 모든 연관관계는 자연로딩!

  • 즉시로딩: 하나의 엔티티를 로딩하는 시점에 연관된 엔티티를 모두 로딩함
    • 즉시로딩(EAGER)는 예측이 어려움 > SQL 실행 추적이 어려움
  • 실무는 모든 연관계를 자연로딩(LAZY)으로 설정
  • 최적화는 fetch join 또는 엔티티 그래프 기능 사용
  • @~ToOne 관계는 디폴트로 즉시로딩이라 직접 자연로딩으로 설정해야 함

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

  • private List orders = new ArrayList<>();
  • null 문제 prevent
  • 하이버네이트는 엔티티를 영속화할 때 변경된 컬렉션을 추적해야하기 때문에 본인이 추적할 수 있는 내장컬렉션으로 변경함 > 임의의 메서드에서 컬렉션을 잘못 생성하면 하이버네이트 내 메커니즘에 문제

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

  • 카멜 케이스 > 언더스코어 (예) memberPoint > member_point
  • . (점) > _ (언더스코어)
  • 대문자 > 소문자

5. Cascade

  • (예) @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    • 클래스를 persist할 때 해당 필드도 같이 persist된다
    • 삭제도 같이 됨

6. 양방향 연관관계에는 컨트롤하는 클래스 쪽에 편의 메소드

  • (예) 주문-회원 관계: 주문에 setMember, 주문-주문상품 관계: 주문에 addOrderItem 등
profile
우당탕탕

0개의 댓글